Google App Engine's Virtual File System with OpenBD
ColdFusion, Internet, Web Development, BlueDragonWorking with the virutal file system on Google App Engine is fairly simple once you grasp the fact that all of your directories and files are really just BLOBs in the Google datastore. If you have worked with binary data from a database before, then this should be quite easy for you.
Interaction with the filesystem is easily done through openBD's implementation of the cfdirectory and cffile tags. You can CREATE and LIST directories from your VFS using the cfdirectory tag you know and love.
The "tricky" part comes when you find that none of the files you have uploaded are web accessible. In other words, you can upload to "/images" but you aren't going to be able to browse to "/images/mynewimage.jpg".
One solution would be to create a .cfm template to take a URL query string and serve up the requested file from the VFS.
For example: getfile.cfm?f=mynewimage.jpg
Which you could then use as the SRC in an IMG tag.
<img src="getfile.cfm?f=mynewimage.jpg" >
Where getfile.cfm might look like this:
<cffile action="readbinary" file="#ExpandPath('/images/' & url.f )#" variable="myFile">
<cfcontent type="#getMimeType(url.f)#" variable="#myFile#" reset="true" >
Note: I created this method called getMimeType() for an example site:
<cffunction name="getMimeType" > <cfargument name="fileName" /> <cfset var extension = ListLast(arguments.fileName,".") /> <cfswitch expression=".#extension#"> <cfcase value=".bmp"> <cfreturn "image/bmp"> </cfcase> <cfcase value=".css"> <cfreturn "text/css"> </cfcase> <cfcase value=".js"> <cfreturn "text/javascript"> </cfcase> <cfcase value=".jpg"> <cfreturn "image/jpeg"> </cfcase> <cfcase value=".jpeg"> <cfreturn "image/jpeg"> </cfcase> <cfcase value=".jpe"> <cfreturn "image/jpeg"> </cfcase> <cfcase value=".doc"> <cfreturn "application/msword"> </cfcase> <cfcase value=".docx"> <cfreturn "application/msword"> </cfcase> <cfcase value=".gif"> <cfreturn "image/gif"> </cfcase> <cfcase value=".gz"> <cfreturn "application/x-gzip"> </cfcase> <cfcase value=".htm"> <cfreturn "text/htm"> </cfcase> <cfcase value=".html"> <cfreturn "text/html"> </cfcase> <cfcase value=".ico"> <cfreturn "image/x-icon"> </cfcase> <cfcase value=".mov"> <cfreturn "video/quicktime"> </cfcase> <cfcase value=".mp3"> <cfreturn "audio/mpeg"> </cfcase> <cfcase value=".ppt"> <cfreturn "application/vnd.ms-powerpoint"> </cfcase> <cfcase value=".pps"> <cfreturn "application/vnd.ms-powerpoint"> </cfcase> <cfcase value=".tgz"> <cfreturn "application/x-compressed"> </cfcase> <cfcase value=".txt"> <cfreturn "text/plain"> </cfcase> <cfcase value=".wav"> <cfreturn "audio/x-wav"> </cfcase> <cfcase value=".wmv"> <cfreturn "video/x-ms-wmv"> </cfcase> <cfcase value=".xls"> <cfreturn "application/vnd.ms-excel"> </cfcase> <cfcase value=".xlsx"> <cfreturn "application/vnd.ms-excel"> </cfcase> <cfcase value=".zip"> <cfreturn "application/zip"> </cfcase> <cfcase value=".xml"> <cfreturn "application/xml"> </cfcase> <cfdefaultcase> <cfoutput>#extension#</cfoutput> FILE TYPE NOT SUPPORTED <cfabort> </cfdefaultcase> </cfswitch> </cffunction>
Coldfusion on the Google App Engine with Open BlueDragon
ColdFusion, Web Development, BlueDragon, Internet, Google App EngineThe future is now!
A little melodramatic maybe, but this technology is exciting. Free cfml app servers with clustering (including data and file storage).
First, if you don't know what the Google App Engine is yet, go here first and do a little reading. Once you have read enough of that to be sufficiently excited, we need to set up the development and deployment tools. Paul Kukiel has put together a really nice demo on how to do this here. NOTE THERE IS ONE THING THAT IS INCORRECT IN THE VIDEO Do not delete the "war" directory, merely paste the openbd war over the existing one. This is important.
Next, reading and writing data with the Google datastore.
Storing data in a scalable web application can be tricky. A user could be interacting with any of dozens of web servers at a given time, and the user's next request could go to a different web server than the one that handled the previous request. All web servers need to be interacting with data that is also spread out across dozens of machines, possibly in different locations around the world.
Thanks to Google App Engine, you don't have to worry about any of that. App Engine's infrastructure takes care of all of the distribution, replication and load balancing of data behind a simple API—and you get a powerful query engine and transactions as well.
Thanks to the fine people at Open BlueDragon, this task is made very very simple. Every cfc in the openBD GAE inherits the following methods from component.cfc. GoogleWrite(), GoogleRead(), and GoogleKey().
-- Example object Status.cfc:
<cfcomponent displayname="Status" output="false"> <cfproperty name="Message" displayname="Message" type="string" /> <cfproperty name="DateTimeCreated" displayname="DateTimeCreated" type="date" /> <cffunction name="init" access="public" output="false" returntype="Status"> <cfreturn this/> </cffunction> <cffunction name="getMessage" access="public" output="false" returntype="string"> <cfreturn this.Message /> </cffunction> <cffunction name="setMessage" access="public" output="false" returntype="void"> <cfargument name="Message" type="string" required="true" /> <cfset this.Message = arguments.Message /> <cfreturn /> </cffunction> <cffunction name="getDateTimeCreated" access="public" output="false" returntype="date"> <cfreturn this.DateTimeCreated /> </cffunction> <cffunction name="setDateTimeCreated" access="public" output="false" returntype="void"> <cfargument name="DateTimeCreated" type="date" required="true" /> <cfset this.DateTimeCreated = arguments.DateTimeCreated /> <cfreturn /> </cffunction> </cfcomponent>
-- Writing data to the datastore:
<cfscript> //saving a new Status to Google datastore Status = createObject( "component", "model.Status" ).init(); Status.setMessage( "I love Google App Engine and OpenBD!" ); Status.setDateTimeCreated( Now() ); /*now all we do is call the googleWrite() method on our object, notice this returns the objects new google key*/ googleKey = Status.googleWrite(); </cfscript>
-- Querying the datastore: for more on this visit the openBD wiki page on the datastore
<!--- notice dbtype="google" and the quasi-SQL ---> <cfquery dbtype="google" name="result"> Select from Status </cfquery> <!--- The result of this query, is an array of matching Status objects. Not the usual query recordset. --->
Securing your new web app with the UserServiceFactory (com.google.appengine.api.users.UserServiceFactory)
Once I figured this step out, it was almost embarassingly easy to secure a page, allowing access only to validated Google account holders.
<cfscript>
UserServiceFactory = CreateObject("java","com.google.appengine.api.users.UserServiceFactory");
User = UserServiceFactory.getUserService().getCurrentUser();
/*Here I am doing a test to see if there is a valid user object returned, aka "logged in". At this time, I haven't found the ideal solution for this*/
isLoggedIn = false;
try{
user.getEmail();
isLoggedIn = true;
}
catch (any excpt){}
</cfscript>
<!---
building login/logut links
--->
<cfif NOT isLoggedIn>
YOU NEED TO <a href="<cfoutput>#UserServiceFactory.getUserService().createLoginURL(toString("http://#cgi.SERVER_NAME#"))#</cfoutput>">LOGIN</a>
<cfelse>
<cfoutput>#request.user.getEmail()#</cfoutput>: All your email are belong to us
<br />
<a href="<cfoutput>#UserServiceFactory.getUserService().createLogoutURL(toString("http://#cgi.SERVER_NAME#"))#</cfoutput>">LOGOUT</a>
</cfif>
Time to build some real applications. Early indications from some experimentation by Dave Shuck, are revealing that the Mach-ii MVC framework along with the Coldspring IOC framework are working on the Google App Engine.
Other features, new or otherwise:
There is just no reason that we as cfml developers shouldn't be churning out app after app on this platform.
Mixing a little Flex with my Mach-II
ColdFusion, MachII, Web Development, FlexOne of my latest projects was to create a little contact manager / sales tool to integrate with an existing system (written in Mach-II). Requirements dictated that I needed to have access to my already logged in user (must be aware of client session). "Down the road" requirements, are that we'd like to make an AIR port of this new feature as a standalone application. As a big fan of Flex, I thought it would be a great opportunity to test the efficacy of writing this new feature as a drop in Flex mini-application.
Since security is handled by the existing application, we needed to make sure the functionality of this app respected the existing security guidelines. The best way I could think of was also the easiest...just start calling events and see what happens.
Lucky for me, it all just worked. So here are a few examples that might help get you started if you are working on the same sort of project.
On creationComplete I call a method named "init()" to get that user's set of contact data:
private var myLoader:URLLoader;
public function init():void
{
var myReq:URLRequest = new URLRequest('/index.cfm/event/GetContactData);
myLoader = new URLLoader()
myLoader.addEventListener(Event.COMPLETE, dataComplete);
myLoader.load(myReq);
}Important: Notice the event listener I added to call the dataComplete method once the request was completed.
Once we have that initial set of data, all that is left is to start POSTing the create/edit/deletes the user is making to his contacts.
Here is an example of doing an HTTP POST request and passing my Contact object to a Mach-II event:
private function saveContact(Contact:ContactVO):void {
var myHTTPService:HTTPService = new HTTPService;
myHTTPService.method= "POST";
myHTTPService.url = '/index.cfm/event/SaveContact';
myHTTPService.addEventListener(ResultEvent.RESULT,function():void{init()});
myHTTPService.send(Contact);
Alert.show('Contact has been saved.');
}Note: Check out how we send the Contact object without doing anything tricky? All of the properties of my ContactVO are available as event Args in my Mach-II listener.
Here is another example of doing an HTTP POST request, but passing individual variables:
private function submitNote(htmlText:String,plainText:String,Contact:ContactVO):void
{
var myHTTPService:HTTPService = new HTTPService;
var obj:Object = new Object();
myHTTPService.method= "POST";
myHTTPService.url = '/index.cfm/event/saveNote';
obj['notetext'] = htmlText;
obj['notepreview'] = plainText;
obj['contactid'] = Contact.ContactId;
myHTTPService.addEventListener(ResultEvent.RESULT,function():void{init()});
myHTTPService.send(obj);
}Note: Notice how we create an object and define the properties we want to send, and then pass that new object in the POST.
Looking to hire ColdFusion/Flex Developer (Addison, TX)
ColdFusion, Web Development, FlexI am looking to hire a full time (on site only) ColdFusion developer.
The type of applications we work on range from support of old legacy applications to object oriented ColdFusion business layer with Flex 3 UI.
Experience working in frameworks is a major plus.
Industry: Mortgage software
Location: Addison, TX
Start Date: Immediate
Salary: Depends on Experience
About FICS:
FICS is a small, family owned company (50ish employees). Founded in 1983 and headquartered in Dallas, Texas, Financial Industry Computer Systems, Inc.
(FICS®) specializes in providing flexible, comprehensive residential and commercial technology
solutions to the mortgage industry. FICS' solutions are designed around the latest technology,
while incorporating innovative imaging and Web-based capabilities into its full suite of products.
Job Function:
- Troubleshooting/enhancing/support of existing web applications.
- Developing primarily in ColdFusion with more and more Flex/ActionSript as we are moving towards Flex as our main UI technology.
- HTML, JavaScript, CSS, minor image manipulation/creation (buttons, etc)
Requirements:
- At least 2 years ColdFusion experience
- Web design/layout experience with HTML, CSS and JavaScript
- ActionScript/Flex experience is a plus
- A good troubleshooting ability is necessary.
Please send resumes to aaronjlynch AT gmail DOT com
Thanks!
Aaron
Combine and compress your javascript files: Scriptalizer.com
ColdFusion, Internet, Web Development, JavascriptAfter creating a custom tag and minifier component (using YUICompressor) I decided it would be a pretty neat service to offer to everybody. Last night I scrounged up a website and called it Scriptalizer.com.
If you want to test what cf_scriptalizer does to your javascript before you actually use the tag, you can try out the generated script provided by Scriptalizer.com.
NOTE: All source files uploaded is immediately deleted once generated.
Problem: WAY too many javascript files. Solution: cf_scriptalizer
ColdFusion, Internet, Web Development, JavascriptWe all know JQuery is awesome right? That should be common knowledge by now. But, if you have ever created an app that makes use of several of the fantastic plugins available for JQuery, then you are going to end up with multiple
So, why is this a problem?
First of all, there is a little bit of bloat going on in this scenario. One could easily end up with over 200Kb of javascript in an assortment of these files. The other thing to consider is that the browser can only make so many concurrent connections to the web server (each external javascript file is one connection by the way) so add all of these files, a bunch of images, a couple .css files and we are making that user wait longer than really necessary.
Lets reduce the number of browser - to - server connections.
That seems easy enough. Instead of multiple external script calls:
<script type="text/javascript" src="/js/jquery/jquery.js"></script> <script type="text/javascript" src="/js/jquery/jquery.form.js"></script> <script type="text/javascript" src="/js/jquery/jquery.jqModal.js"></script> <script type="text/javascript" src="/js/jquery/jquery.history_remote.pack.js"></script>
We could combine them all into one big javascript file and reference it:
<script type="text/javascript" src="/js/myBigScript.js"></script>
Well what happens when you want to make a change to one of the source js files? Or you need to change the order of how the files are included? Or maybe the javascript files needed vary based off of where you are in your app and you don't want to include ALL of the files every time? Thats where I think I can help...
I have just recently (as in today) finished up a custom tag to do just that. For lack of a better name I called it
<cf_scriptalizer filePrefix="myscript" scriptalizerDirectory="/js" scriptFileList=" /js/jquery/jqModal.js, /js/jquery/jquery.js, /js/jquery/jquery.MultiFile.js, /js/jquery/jquery.blockUI.js, /js/jquery/jquery.corner.js, /js/jquery/jquery.form.js, /js/jquery/jquery.history_remote.pack.js, /js/jquery/jquery-dom.js " reload="true" >
- filePrefix (optional): prefix of the generated javascript file. "scriptalizer" by default
- scriptalizerDirectory: relative path to the desired output location
- scriptFileList: list of js files in the order you would include them
- reload (optional) - scriptalizer will inspect each js file for changes made since last access
More about reload: When/if you pass reload=true, <cf_scriptalizer>
Now that we have reduced the number of connections for external javascript files down to just one for this request, what can we do about reducing the size.
<cfset minifier = CreateObject("component","com.cfyuiminifier.cfYUIMinifier").init(path="/com/cfyuiminifier")/>
<cf_scriptalizer
filePrefix="myscript"
scriptalizerDirectory="/js"
scriptFileList="
/js/jquery/jqModal.js,
/js/jquery/jquery.js,
/js/jquery/jquery.MultiFile.js,
/js/jquery/jquery.blockUI.js,
/js/jquery/jquery.corner.js,
/js/jquery/jquery.form.js,
/js/jquery/jquery.history_remote.pack.js,
/js/jquery/jquery-dom.js
"
minifierObject = #minifier#
reload="true"
>As you can see, I am passing in the object "minifier" that contains the method minify() that accepts a string of all of the comibined javascript and returns a string of the minified javascript.
This additional component was abstracted from the custom tag itself just in case the user has some issue with YUICompressor or wants to implement his/her own solution to compress/clean/minify/etc the source javascript.
In this example, the combined jquery files resulted in a 123.1 KB javascript file (pre-minification). But, once cfyuiminifier.minify() was run on the source, we ended up with a combined javascript file that was only 79.7 KB!
CAVEATS:
I have not yet run this on any live sites.
This is brand new code from my scribble pad, so please be aware you may encounter some bugs!
This was developed on my Linux machine and haven't tested it anywhere outside of this environment.
DOWNLOADS:
If anybody has any specific requests for features, or would like to contribute please let me know. I wrote this in an attempt solve a specfic need for some applications I have been working on, and ended up with something I thought might be beneficial to others. Please report back with any successes (or failures) with this tool...I am very interested in hearing about your experience(s).





Loading....