Over the course of the past week I've been working on a new onTap framework plugin that will download and install Mango Blog into an onTap framework application.
Building on the previous article where I announced the release of a Fusebox importer, I've now released a similar ColdBox importer that allows you to run ColdBox applications within the context of an onTap framework application as well as a Transfer ORM plugin that downloads, configures and installs Mark Mandel's ORM framework.
I think this is really starting to show how the onTap framework accomplishes one of my long-term goals of encouraging greater collaboration within the community.
In one way ColdBox turned out to be slightly less helpful than Fusebox in creating this integration, because there's no programmatic way for me to set the "AppMapping" setting in the Application.cfc, which means that the installer can't actually do everything for you, you have to set the AppMapping setting yourself in the ColdBox config xml. Bummer. Maybe a future version of ColdBox will allow that.
So for example if I installed a forum written in ColdBox and I wanted to sync-up the user database from the forum with my own user management in the onTap framework (check out the Members onTap plugin) I can look in my user management directories in my own application and I can slip in a handy little call to the ColdBox RunEvent() method any time I create a new user or update a user, to keep the two systems in-sync for a single-sign-on application.
On a similar note, I've also just published a new Transfer ORM Plugin. I found a handy little Transfer Config project on RIAForge from Rolando Lopez, which uses the new CFDBINFO tag in CF8 to introspect your database and generate the required XML config file for the Transfer ORM.
What's really neat about this is that now you can get Transfer all set up and running in your onTap framework application with zero coding (and very little downloading). You just head over to your handy plugin manager, search for new plugins on the webservice, select the Transfer plugin and press the install button. After you've accepted the license agreement, the plugin will optionally download the latest version of Transfer and install it for you. Then it presents you with a small form with some options for how you'd like to generate your transfer config file and viola! You're ready to get transfer objects in your onTap application. :)
Kudos to Rolando for helping to make this possible, and of course to Mark Mandel for giving us Transfer.
Last year I posted an entry about a CMS plugin for the onTap framework (and I'm still surprised at how much traffic it got). Unfortunately that particular project hasn't become a reality yet, but after having just finished up a revision of the onTap framework and the DataFaucet ORM that both now include integration with the new CacheBox cache-management framework, I decided to start working on plugins again.
Something that came up in that talk about a CMS plugin was how the Sava CMS has managed to create integration with a variety of other ColdFusion frameworks, including Fusebox, ColdBox and Mach-II. I think that shows marked resourcefulness and is a direction I'd like to see the ColdFusion community move more toward. And it's something I've thought about off and on over the past year, but I've not gotten around to doing anything practical about it until now.
Amazingly it's only taken me two days to put together a first release of a Fusebox Importer plugin. Tony Garcia mentioned before that integration between apps in the PHP world actually has a name, they call it "bridging". And that's a fairly good description of what I'm doing here. You install the Importer plugin and from that point, you can then manage fusebox applications that are imported into your onTap framework application through the importer, which acts as a kind of bridge between the Fusebox framework and the onTap framework.
I was hoping that I might be able to find a full-fledged application to test this with, but unfortunately didn't actually find that. I thought maybe there would be a blog or a forum or something available that was written in FB5, but couldn't find one. I did of course find examples of both of those things, but only in older versions of Fusebox (4.1 or 3). So I ended up testing this with a copy of the CatClub sample application that Sean provided when he was lead on the Fusebox project.
I did have to make a few small, yet important modifications to the framework core to get this bridge to work. So if you're interested in trying it out, you'll have to download the latest framework core release (which I just uploaded a few minutes ago). Then use the search form in the plugin manager to find and download the Fusebox Importer.
I'll probably have a look at ColdBox and Mach-II soon for the same thing. If they're similarly easy to integrate, I'll be a very happy man indeed. :)
The reason I say this is a perfect candidate is because until now there hadn't been a complete, simplified example of a plugin installer. There have been plugins certainly. There's the DataFaucet plugin and the Members onTap plugin and now there's a CacheBox plugin, but all of those plugins are whole frameworks in their own right. That's been a problem because although the plugin system is documented, there wasn't a complete example of a plugin that you could use as a point of reference for what a plugin should look like. You had to guess about whether or not you were reading the documentation correctly. And looking at the DataFaucet plugin or the Members onTap plugin might not have been terribly helpful, because both of those are larger systems with relatively sophisticated installers, so it would have been easy to get lost in the code and have difficulty separating the installer from the application as you're looking at it. So by creating a plugin from something that arguably doesn't actually need a plugin, I've been able to provide a complete, simplified example that shows you just how to write an installer and nothing much else. ;)
Rather than publishing it separately, I've included it in the Plugin Manager project, which is now part of the core framework download. I also made a few very minor changes to the Plugin Manager itself - I don't think you'll notice any functional differences. But if you just want to see the plugin example, I'm attaching the zip archive of the Google Analytics plugin to this blog as well. Look for the download link in the stats below this entry.
Something I've tried to promote more recently in the community is that much like the other frameworks, the features of the onTap framework can be extended via "plugins". That aspect is not new. What's new about the onTap framework is that the term "plugin" is used to describe a much more powerful entity.
There's a page on the framework wiki that describes this difference in greater detail, but basically in Mach-II, Fusebox or ColdBox a plugin typically consists of what you can fit in a single CFC. So a plugin in those frameworks may be used within your controllers or your views, but generally won't have its own controllers or views. Some examples from the ColdBox Wiki include: FileWriter, Paging and Captcha. You can see that these are tools you might use within an application, but they aren't applications in their own right.
The onTap framework alread has one plugin that falls in this category, it's called TapMCE for a WYSIWYG editor (and admittedly, I'd like to overhaul the idea). But that's not the primary focus of plugins in the onTap framework. The primary focus in the onTap framework is on peer or sub-applications. Currently you'll find that the onTap framework includes a Members onTap plugin which is a security layer, but is also much, much more than that. The Members onTap plugin also includes a sign-up form and login with optional OpenID support, a lot of internationalization work for managing sites or applications where the users speak more than one language, customizable regional information for your company (country, state, city, plus custom regions like "midwest" or "sales territories"), etc.
I've uploaded a new version of the framework core with some updated code for the plugin manager. It shouldn't have a huge effect, I've simply added a manifest to the plugin manager that allows it to store version information of installed plugins.
I also updated all the plugins I'd previously published. The reason is because prior to the manifest, plugins were responsible for reporting to the application whether they had been installed and what version they were. That worked, but it lead to a number of questions on my part about how to create upgrade installers for newer versions of a plugin. The manifest simply copies the version information when the plugin is installed, allowing the manager to compare version reported by the plugin against the version stored in its manifest to determine if a new version is waiting to be installed. This way if there's a new plugin waiting, the old version will appear in the "installed" tab while the new version appears simultaneously in the "new" tab.
Long story short, it makes writing updates for plugins easier. :)
If you're already using the plugins with the previous release then after extracting this release, you may need to either reinstall your plugins or add version information to the manifest manually.
I'm also preparing to do some additional work in the DataFaucet core to streamline schema updates. So for example if you have a new version of your plugin and it needs a new column in a particular table, you can just copy over the CFC for the active record, instantiate one and call ActiveRecord.install() to add any new columns. I'm also contemplating some semantics for allowing it to generate the DDL from cfproperty tags similar to FarCry data objects. It's mostly about making installations easier, although the cfproperty features will also add some transparency.
I just noticed on the FarCry documentation site that Geoff Bowers has proposed a number of things for FarCry that are part of the current 3.2 release of the onTap framework. Not much in the way of details yet, he even described them as "rudimentary thoughts". I just thought it was interesting to see that Geoff was leaning in that direction.
- dedicated plugin install/remove engine (yup, got it)
- dedicated plugin data import engine (not sure what this means - likely FarCry specific)
- plugin listing and deployment window (yep, there's a list and deploy page in the plugin manager)
- hooks for external web services directory of plugins (yep, got it -- it connects to this webservice)
You can see Geoff's notes in The Cage (their enhancement-requests area) under the heading "Plugin Explorer".
Oh and speaking of which, if you haven't already seen it, there are a couple of videos showing the installation of plugins from the webservice on the official framework site.
Also the Plugins Guide on the framework wiki describes the difference between an onTap framework plugin and a plugin for other frameworks like FuseBox or Mach-II. Apparently FarCry and the onTap framework have similar definitions of a Plugin. In other words, with both FarCry and the onTap framework you might have "content management system (CMS)" as a plugin. I don't think you would ever see that as a plugin for FuseBox or Mach-II because the label "plugin" is used to describe a different kind of extension.
Last week I wrote an article titled SOA Simplified in which I attempted to provide a short and relatively simple explanation of "service oriented architecture". The Devon CFUG Manager, John Whish commented that he'd always thought of SOA as "communicating with web services". While it's true that web services are an example of SOA it's something of a misnomer to think of them as being synonymous. Being service oriented really just means designing an architecture that allows services to be easily discovered and used by other entities (maybe other services). So there are lots of examples of SOA in the wild that have nothing in particular to do with web services.
Last week I mentioned the IoC Manager in the onTap framework as being one of the ways the onTap framework provides SOA within an application. Today I'd like to elaborate on that a little bit.
It's become common in the ColdFusion community to use Inversion of Control (IoC) frameworks in our applications. For the uninitiated an IoC framework mostly just creates objects. The reason this is so useful (and important) is because it provides a single place where these objects can be created and manages the dependencies between them (and should probably be called "Dependency Injection (DI)" although the acronym hasn't really caught on). The 800-pound gorilla in the ColdFusion community is ColdSpring although Pete Bell's LightWire has also been around for a while.
So here's a before and after to show what an application looks like with or without an IoC framework:
<cfset dep1 = application.dep1 />
<cfset dep2 = application.dep2 />
<cfset thingService = CreateObject("component","com.myCompany.thingService").init(dep1,dep2) />
<cfset thingService = application.IoCFactory.getBean("thingService") />
The word "bean" in the IoC factory example is an unfortunate convention which I believe started with Java. A much better name for that method would be "getObject", but I use getBean merely because it's become the defacto standard for IoC frameworks. The word "bean" really doesn't mean anything -- they're just objects.
In a typical application the majority of objects managed by an IoC framework are "singletons", which is a fancy way of saying the application should only have one of them. In a pre-ioc application these objects are typically created when the application starts (in the onApplicationStart event of Application.cfc). This ensures that there is only one of each of these objects for the entire application because onApplicationStart only executes once and so each object is only created once.
This is called "aggressive loading" because everything is created or "loaded" well in advance.
While this works for small applications, as the size of the application grows it takes longer for the application to start. And just because the application may need a particular object doesn't mean that object will be used on every request. For example your application may have several areas like RIAForge which has blogs, forums, wikis and bug trackers. When the server is idle a request for any page on the site starts the application and aggressively loads all its objects. So if a user visits the blog, the application will load not only the objects for the blog, but also for the wiki, the forum and the bug tracker even though none of those other objects will be used. Even though the visitor isn't using them, he's still forced to wait while they load. (The RIAForge site doesn't work that way, this is just an example.)
This leads to what is probably the second most important purpose of an IoC framework, which is "lazy loading".
As you might imagine lazy loading is an alternative to aggressive loading. As an application grows and becomes more complex, a given page request will use a smaller percentage of its business objects. In the above example when a visitor requests a page of the blog, that request will use only about a quarter of the business objects (with forum, wiki and bug tracker being unused). So because each request will use only a small number of objects, a lazy-loading system will create these objects only when they are needed or in other words "on demand". For this reason IoC frameworks usually default to lazy-loading all objects unless otherwise specified. This way when the application loads, only the IoC factory needs to be loaded in advance. As the application grows and becomes more complex, the IoC factory remains the only aggressively loaded object and visitors are waiting only for the objects that are needed to deliver their content.
The popular frameworks for ColdFusion, the "big 4" (Fusebox, Mach-II, Model-Glue and ColdBox) include integration for IoC frameworks. What differentiates the onTap framework is that where the big 4 focus on having a single IoC factory for your application, the onTap framework adds another layer called an "IoC Manager" which places the emphasis on having multiple IoC factories for different "services" within your application. This is a large part of what makes the onTap framework an SOA approach to ColdFusion development. To make it clear, it's not the fact that you can have multiple IoC factories in the onTap framework that makes it SOA. There's nothing in the big 4 frameworks that might prevent you from using multiple IoC factories, there's just nothing in them to make it easier. What makes the onTap framework SOA is the fact that there is an extra layer that standardizes management of IoC factories, providing easy and consistent methods of discovering, accessing and utilizing those factory "services".
Ultimately this basically boils down to the definition of a "plugin" because it's fundamentally different for the onTap framework as compared to the big 4. A plugin for the big 4 frameworks is a single CFC. A plugin for the onTap framework is a sub-application like a blog or a forum, composed of multiple files. In ColdBox specifically your IoC factory (ColdSpring or LightWire) is configured as a plugin. In the onTap framework, each plugin can (and typically will) have its own IoC factory.
The ColdBox config XML file contains three properties for configuring your IoC factory: IoCFramework, IoCDefinitionFile and IoCObjectCaching. This loads a single IoC factory which you can then fetch later with getPlugin("IoC") and from there you can request your singletons with IoCFactory.getBean("nameOfBean").
This works well to accomodate applications that place an emphasis on the programming work being done by your own development team. If your application has a forum, your team built it. If it has contacts, your team built it. If it has ecommerce, your team built it. If it has blogs, wikis, bug trackers, your team built them. This is an exaggeration, but only a very small exaggeration.
You're likely to use tools developed by other people, such as for AJAX (jQuery, Spry, etc) or custom tags for various display widgets, so I'm not talking about NIH Syndrome. These are however very small and minor helpers. However easy they might be to use, your programming team still needs to work to make them useful. A custom tag or an AJAX framework is great, if the bulk of your development is already done - if you already have a database schema, if you already have business objects, etc. You're unlikely however to use business objects or database schemas created by anyone outside your team, which is the bulk of any given application.
Unless your company is a social networking site like Ning or Tribe, it's unlikely at best that "forums" are part of your core business. That being the case, why should your team be tasked with creating a forum? The same is true of most of these small one off kind of features. Your application stores contact information or a wiki or a help system or a knowledge base. Why have your own developers devoting their time to these tasks that are non-essential to your core business? Why not leverage the community?
The reality of working in the ColdFusion community today is that it's difficult to get any kind of collaboration on these kinds of sub-applications, because the popular frameworks only focus on the work done within your own programming team. There's been no attention given to the idea of bringing services in from outside. Single sign-on works, but it's a weak solution. It has to be developed and integrated by your team because generally speaking there's not standardization. There's at best minimal communication between the sub-apps, and it does nothing to enrich the user experience. In fact it often detracts from the user experience because the forum doesn't look or feel like the rest of the application, etc.
So back to the example, ColdBox is a good example of the approach taken by the "big 4". I mentioned before that ColdBox includes three parameters in its XML config file. When the application loads (onApplicationStart), ColdBox creates a single IoC factory that you can later use via getPlugin("IoC"). By comparison the onTap framework doesn't have an XML config file. It offers a directory structure for configuring any IoC factories you might need for your application. So in the /_tap/_config/ioc/ directory you might have several CFCs named things like forum.cfc, blog.cfc, wiki.cfc etc. or better yet they would be named something more specific like "mangoblog.cfc". Each of these config objects declares an IoC factory needed by some part of your application.
The config object looks like this:
<cffunction name="configure" access="public" output="false" returntype="void">
<cfset newContainer(name="MyAppName",className="ColdSpringAdapter").init("/path/to/coldspring/config.xml") />
These created containers are then later fetched with request.tap.getIoC("nameOfContainer") or you can check to see if a container exists with request.tap.getIoC().hasContainer("nameOfContainer"). This allows you to use whatever IoC framework you want to use and the onTap framework itself offers its own default IoC factory class which is much, much simpler than either ColdSpring or LIghtWire. The built-in IoC factory class is not intended to compete with other IoC frameworks, it's just offered as a simplified alternative for people who don't need the extra features and would like to avoid an extra dependency. But the className can be whatever class you want, so you could have a totally hand-written IoC factory if you wanted it and this is the case for both DataFaucet and the PluginManager.
You might notice however that it's described as a ColdSpring "adapter". This component merely declares an IoC factory -- it doesn't actually create one. It does however create an IoC "container". An IoC container in the context of the onTap framework is an object that holds an IoC factory declaration and knows how to create that factory. So what it's actually doing here is lazy-loading the IoC factories. When the application loads, it creates the IoC Manager and through the help of these config objects it declares a set of containers. The actual IoC factories then are merely waiting for a call that says "hey, I need that blog service!" The manager then returns the blog service container and it isn't until the container is asked for a specific "bean" (object) that the factory is created. That factory may be a ColdSpring factory, it may be a LightWire factory or it may be something all together different. Your application doesn't care. It just knows that it has a blog service.
So this basically has taken the concept of IoC and moved it back a step from the application. In essence, it's a standardized IoC factory for your IoC factories. :)
The really nice thing here is that this makes it easy for people in the community to bundle up their applications as plugins. Each plugin is likely to have an IoC config object like I've just shown that declares the IoC factory needed for that plugin. This IoC declaration immediately makes that plugin available as a "service" to the rest of your application. So if you then want to integrate your calendar sub-application with your contact system, its really easy to do that because most of the work is already done. You can fetch the service objects for your calendar with request.tap.getIoC("calendar") or you can fetch the service objects for your contact system with request.tap.getIoC("contacts"). Then it's a simple matter of wiring them together in your controller. What could be easier? :)
Today we've released version 1.0 beta of the FireLadder scaffolding tool. This plugin requires DataFaucet version 1.0. We also discovered and fixed a new issue in DataFaucet while developing it, so if you alreaady have DataFaucet installed, now's a good time to update. :) And there were a couple of small issues discovered and fixed in the onTap framework core also, so while it may not be necessary, an update of the onTap core distribution wouldn't hurt either.
So you're probably wondering, "where do I download it?"
The answer - it's on the framework webservice. Just go to your onTap framework application wherever you installed it, load up the plugin manager, select the "more" tab, hit "search" and select the "install" button next to the FireLadder plugin! Find and install it directly within the framework! (Just like Eclipse! Just like Firefox!)
After downloading an extracting the onTap framework 3.2 distribution, your plugin manager can be found at:
Simply replace "localhost/ontap" for wherever you've placed the framework.
Also in case you haven't seen it yet, the installation videos on the framework home page show how to download and install plugins from the webservice.
Although this is a 1.0 release of the scaffolding utility and it is not intended for complex applications, you can expect SOA-style integration. It will create a discrete package of model objects (2 per table), an IoC Container that will be wired into the framework's manager, two views per table and a handful of controller templates.
It was actually quite a bit more complicated than I had expected and turns out to be more complicated than it is for me personally to generally just create simple CRUD apps. A large part of the problem has to do with techniques I don't normally use. For example, I don't normally change the column names in my database when I create my objects - so if I have a table with a column named "productprice", then my object has a property named "productprice" (not just "price"). I also don't create table columns with underscores in my database.
So because the DataFaucet tools recently added features to do both of these things, I found myself being forced to recreate a lot of the automation that was previously built-in to the framework core. The previous automation is still there of course, I just couldn't use it for the scaffolding tool because of the introduction of inconsistencies in naming conventions.
And then I noticed because I was using the Galleon Forums tables to test it, that I also needed a "dePluralizer" because some folks like to name their tables in the plural. This is actually a prime example of why I feel this is a bad idea. Those in favor of plural table names like "galleon_conferences" site as the reason for preferring them that you're naming the table and the table will contain multiples. Okay, that's fine. My problem with this is that this is a non-functional argument. There is no *functional* basis for it.
There are however *functional* arguments for not naming tables in the plural, notably that no matter how much you like the idea of a plural table name, you're not going to name your business objects that way. You're going to name your business objects in the singular and that means ta-da! more work on both your part and on the part of the server, simply because you felt it was "propper" to have plural table names. That's actually a *functional* argument. And you can see why if you look at the FireLadder.cfc in the new scaffolding tool, because it has to dePluralize table names... and it can't even handle all of them because they're not consistent and there aren't any rules that could be applied to distinguish when the plural is created with an "i" vs. when the plural is created with an "s" vs. when the plural is created with "es".
Status Statuses (keep or drop the e?)
Virus Virii (es or ii?)
There are no consistent rules for pluralization and therefore, no way to guarantee successful translation from singular to plural or vice versa. So the end result is that plural table names means more work and a more fragile and less scalable system.
All that being said, there is some support in FireLadder for plural table names.
So I just noticed that since I uploaded some screen captures of the Members onTap plugin, there's been a spike in interest. It's views in the last couple days skyrocketed up to nearly 700 views (more than twice as many as the framework core project) and it's had 16 downloads...
There's just one problem...
There've only been 11 downloads of the Plugin Manager...
Which is listed in the requirements for the Members onTap plugin. It also makes me wonder if, even though there are twice as many downloads for the framework core, that some of the downloads for the Members onTap plugin might not have accompanied a download of the core. Anyway... whoever's getting it by itself is going to be awful confused if they don't read the installation instructions and figure out that they need the supporting architecture.
Edit (Dec. 27): Downloads for the plugin manager seem to be catching up with downloads for the member plugin, so I guess folks are realizing from the readme.txt that they need it. I'm still surprised honestly that adding screen-shots made the number of views for the member plugin ramp up like that. Views are up over 1k now.