CFAbort And OnRequestEnd() Behavior In ColdFusion 8 And ColdFusion 9

Posted June 29, 2011 at 10:31 AM by Ben Nadel

Tags: ColdFusion

A couple of months ago, I blogged about changes in ColdFusion 9 that allowed the onRequestEnd() event handler to be called after a CFLocation tag. In the comments to that post, David Boyer brought it to my attention that, as of ColdFusion 9, the CFAbort tag worked the same - that is, that it allowed the onRequestEnd() event to execute. A CFLocation tag I can understand; but, a CFAbort tag? This seems to fly in the face of the very intent of an "abort" action. I had to see this with my own eyes.

To test this behavior, I set up a simple Application.cfc ColdFusion framework component and an index file. The Application.cfc simply logs the existing event handlers; the index.cfm file aborts the current request. Let's look at the Application component first:

Application.cfc - ColdFusion Framework Component

  • <cfcomponent
  • output="false"
  • hint="I define the application settings and event handlers.">
  •  
  • <!--- Define the application settings. --->
  • <cfset this.name = hash( getCurrentTemplatePath() ) />
  • <cfset this.applicationTimeout = createTimeSpan( 0, 0, 5, 0 ) />
  •  
  •  
  • <!--- Define the local file path for the logging. --->
  • <cfset this.logFilePath = (getDirectoryFromPath( getCurrentTemplatePath() ) & "log.txt") />
  •  
  • <!---
  • Determine the ColdFusion engine prefix to use in the logging
  • (so we can run this on ColdFusion 8 and ColdFusion 9).
  • --->
  • <cfif find( "cf8", cgi.server_name )>
  •  
  • <!--- ColdFusion 8 configuration. --->
  • <cfset this.engine = "CF8.0.1" />
  •  
  • <cfelse>
  •  
  • <!--- ColdFusion 9 configuration. --->
  • <cfset this.engine = "CF9.0.1" />
  •  
  • </cfif>
  •  
  •  
  • <cffunction
  • name="onRequestStart"
  • access="public"
  • returntype="boolean"
  • output="false"
  • hint="I initialize the request">
  •  
  • <!--- Log the request start. --->
  • <cffile
  • action="append"
  • file="#this.logFilePath#"
  • output="#this.engine# - onRequestStart"
  • addnewline="true"
  • />
  •  
  • <!--- Return true so the request can load. --->
  • <cfreturn true />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="onRequest"
  • access="public"
  • returntype="void"
  • output="true"
  • hint="I execute the requested script.">
  •  
  • <!--- Log the request. --->
  • <cffile
  • action="append"
  • file="#this.logFilePath#"
  • output="#this.engine# - onRequest - PRE Content"
  • addnewline="true"
  • />
  •  
  • <!--- Include the requested script. --->
  • <cfinclude template="#arguments[ 1 ]#" />
  •  
  • <!--- Log the request. --->
  • <cffile
  • action="append"
  • file="#this.logFilePath#"
  • output="#this.engine# - onRequest - POST Content"
  • addnewline="true"
  • />
  •  
  • <!--- Return out. --->
  • <cfreturn />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="onRequestEnd"
  • access="public"
  • returntype="void"
  • output="false"
  • hint="I teardown the request.">
  •  
  • <!--- Log the request start. --->
  • <cffile
  • action="append"
  • file="#this.logFilePath#"
  • output="#this.engine# - onRequestEnd"
  • addnewline="true"
  • />
  •  
  • <!--- Return out. --->
  • <cfreturn />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="onError"
  • access="public"
  • returntype="void"
  • output="false"
  • hint="I handle any uncaught exceptions.">
  •  
  • <!--- Log the error. --->
  • <cffile
  • action="append"
  • file="#this.logFilePath#"
  • output="#this.engine# - onError"
  • addnewline="true"
  • />
  •  
  • <!--- Return out. --->
  • <cfreturn />
  • </cffunction>
  •  
  • </cfcomponent>

As you can see, this code basically just logs the event handler names to a text file. I am setting up an engine prefix for the logging so we can see which ColdFusion version is being used - I'm running this in both 8.0.1 and 9.0.1.

The onRequest() event handler includes the currently requested script, which in this test, is always index.cfm. The index.cfm is the template that uses the CFAbort tag.

Index.cfm

  • <!--- This is the main index file. --->
  • Hello :)
  •  
  • <!--- Kill the current request. --->
  • <cfabort />

Ok, so this is all pretty simple. When we run this code in ColdFusion 8 and then in ColdFusion 9, we get the following log output:

CF8.0.1 - onRequestStart
CF8.0.1 - onRequest - PRE Content

CF9.0.1 - onRequestStart
CF9.0.1 - onRequest - PRE Content
CF9.0.1 - onRequestEnd

As you can see, the onRequestEnd() event handler was invoked in ColdFusion 9 but not in ColdFusion 8. I really don't know how I feel about this. My gut feeling is that it is a mistake. After all, why would someone use a CFAbort tag? It's not a normal kind of control flow - you use CFAbort when you truly don't want anything else on the page to execute (as opposed to using CFExit or CFReturn). And yet, in ColdFusion 9, the onRequestEnd() tag will be invoked.

While the default behavior is divergent between these two releases of ColdFusion (8 and 9), you can still get them to act the same way by using the CFAbort tag's showError attribute. When you add the showError attribute, the CFAbort tag works more like a CFThrow tag, raising an exception.

Let's update our index.cfm file to use the showError attribute:

Index.cfm (With CFAbort ShowError Attribute)

  • <!--- This is the main index file. --->
  • Hello :)
  •  
  • <!--- Kill the current request. --->
  • <cfabort showerror="Noooooo!" />

Now, when we invoke this in ColdFusion 8 and then in ColdFusion 9, we get the following log output:

CF8.0.1 - onRequestStart
CF8.0.1 - onRequest - PRE Content
CF8.0.1 - onError

CF9.0.1 - onRequestStart
CF9.0.1 - onRequest - PRE Content
CF9.0.1 - onError

As you can see, neither of the ColdFusion engines invoke the onRequestEnd() event handler; however, they now both invoke the onError() event handler to catch the CFAbort-based exception. As such, you'd have to put some control flow logic within your onError() event handler (to not treat CFAbort exceptions as true "errors").

It's probably best to avoid the CFAbort tag in general. The ColdFusion application framework gives you some great hooks to change the way requests are processed (such as returning False from the onRequestStart() event handler). But, that said, I still don't think the new CFAbort behavior in ColdFusion 9 is good; it's outcome does not appear to be in alignment with the intent of the tag.




Reader Comments

Jun 29, 2011 at 10:57 AM // reply »
31 Comments

Thanks for blogging about this ;)

The work around we ended up using where I work involved setting a request variable (e.g. request.complete = true) as the last line of onRequest. Then checking for it in onRequestEnd, if it doesn't exist then cfabort (again!). Of course that only works if you're using onRequest :)

I agree with you though, it doesn't feel right. How else other than cfabort are you supposed stop things processing?


Jun 29, 2011 at 11:05 AM // reply »
11,246 Comments

@David,

That's a pretty good work around. My guess is, they had to add this for some other reason and didn't necessarily catch this as a byproduct.


Jun 29, 2011 at 11:35 AM // reply »
272 Comments

@Ben, as another workaround, where appropriate:

The original Application.cfm and OnRequestEnd.cfm approach still behaves the CF 8 way. CFAbort just stops processing, and OnRequestEnd.cfm is not included. Not sure about CFLocation, but I suspect that also doesn't go through OnRequestEnd.cfm.


Jun 29, 2011 at 11:37 AM // reply »
25 Comments

This breaks backwards compat, so should be considered a bug, yes?

Equally, the docs for CFLOCATION state that it "Stops execution of the current page", so if that's not what it does, that's a bug too.

If they want to change the behaviours for these tags (I see an argument for this), then perhaps an optional parameter, eg:

<cfabort exit="graceful">

Or something like that. But the DEFAULT behaviour should reflect previous behaviour, and indeed the intent of the tag. Something called CFABORT should abort. not "kinda abort a bit, but not really".

:-|

--
Adam


Jun 29, 2011 at 11:39 AM // reply »
25 Comments

Oh for goodness sake.

It's probably due to this:
http://cfbugs.adobe.com/cfbugreport/flexbugui/cfbugtracker/main.html#bugId=80798

That was a dumb E/R.

--
Adam


Jun 29, 2011 at 11:47 AM // reply »
49 Comments

WTF!? cfabort should not fire onRequestEnd.

A "graceful" attribute could be handy, but it should certainly not be the default behaviour.

All the people that voted for and implemented this behaviour need a big slap!


Jun 29, 2011 at 12:06 PM // reply »
1 Comments

Thanks for posting Ben, this has been puzzling me for a while.

Pretty big problem IMHO.


Jun 29, 2011 at 12:32 PM // reply »
11,246 Comments

@Adam,

Ahhh, good find on the Bug Tracker. I wonder what kind of issues Mark had when he suggested it. I would assume he *did* have some valid use case - Mark is a pretty smart dude.

That said, I still think it's a bug :D

And, if you put a CFAbort in the Application.cfc pseudo constructor, neither the onRequestStart() nor the onRequestEnd() fires. If you're going to have logic that say "onRequestEnd() should fire no matter what," wouldn't is *also* make sense that onRequestStart() would also follow that same logic? After all, a request is a request is a request.


Jun 29, 2011 at 1:33 PM // reply »
8 Comments

I think the bug tracker item came from the idea that page processing is not the same as request processing. Although I didn't know about the bug tracker item, I probably would have voted for it, since it never made sense to me that aborting the page processing would also end the request without firing the "request-end" event.

I would think every request should fire the start event and end event, regardless of what occurs in the middle. It really doesn't make sense to me to say "Runs at the end of a request, after all other CFML code (unless the processing of a ColdFusion page ends early)."


Jun 29, 2011 at 1:51 PM // reply »
5 Comments

I've raised a bug for this to get this change backed-out:
http://cfbugs.adobe.com/cfbugreport/flexbugui/cfbugtracker/main.html#bugId=86960

Pls consider voting for it.

--
Adam


Jun 29, 2011 at 1:55 PM // reply »
49 Comments

Mike, whether or not it makes sense, changing the behaviour of this tag in such a way will break many applications, and Adobe always say they take backwards compatibility into account.

There are no applications prior to CF9 that depend on onRequestEnd firing with cfabort, but there will be plenty that depend on it not firing, that now require extra work done.

In Railo, there is a `type` attribute on cfabort which defaults to "request" but can be changed to "page" to act as you describe.

This has been there since Railo 2 (i.e. before CF8 was released), and it would have made far more sense to implement that attribute than to change the behaviour of the tag.


Jun 29, 2011 at 1:56 PM // reply »
5 Comments

Hi Mike
Yep, I would possibly agree if that's the way onRequestEnd() was initially implemented; but it wasn't.

Equally one could - slightly semantically - that a CFABORT condition is not the request *ending*, it's the request being aborted. Not the same thing. Then again I've always thought CFABORT's behaviour of ABORTing *and* flushing the buffer was a daft idea. As soon as a piece of functionality does x AND y, then it's a bad implementation.

If you read my bug entry (as per previous comment), you'll see my position is that CFABORT should *abort*. Immediately. This was always its intent.

If we want to quit processing, then that's more the realm of CFEXIT. A precedent has been set there for that sort of behaviour, so extending it to also deal with exiting the request makes sense.

However one looks at it, what SHOULDN'T have happened is the default behaviour of CFABORT being changed.

--
Adam


Jun 29, 2011 at 2:06 PM // reply »
158 Comments

Sounds like to be safe, you'd need to change all CFAborts that are in your code to a new function that you create that would pass a variable to "onRequestEnd" that says, "Graceful = false"

In OnRequestEnd
<cfif graceful eq false> </cfif>

Yes?


Jun 29, 2011 at 2:27 PM // reply »
49 Comments

Randall,

If you are not currently using the "showerror" attribute for cfabort, you can update all cfabort tags to set this, and then in your onError function you can do:

  • <cfif Arguments[1].TagContext[1].Id EQ 'CFABORT' ><cfreturn/></cfif>

If you currently use the showerror attribute for other purposes, you would probably need to do something like what you say...

Simplest one is probably to replace

  • <cfabort

with

  • <cfset Request.Aborted = True /><cfabort

and then inside onRequestEnd do

  • <cfif StructKeyExists(Request,'Aborted')><cfreturn/></cfif>

(And of course, if you've got any cfscript with abort keyword used, you'd have to do an equivalent replace for that too.)


Jun 29, 2011 at 3:27 PM // reply »
171 Comments

They could also resolve the issue by adding a second argument to the onRequestEnd method which indicates whether or not the request was ended abruptly--like via CFABORT.

That way you could easily add logic to your onRequestEnd() method if you wanted alternative logic for requests that didn't end normally.


Jun 29, 2011 at 4:59 PM // reply »
8 Comments

@Peter,
I wasn't really referring to backwards compatibility. I consider maintaining backwards compatibility a separate issue. Adobe should have done one of two things for that:
1) Maintained backwards compatibility by default and provide the option for the new functionality (basically how everyone had suggested).
2) Made it more apparent that this functionality was changing so it could be tested before people upgraded to the new version. I realize people should be testing that anyway, but I'm fairly certain nobody would know to test to make sure cfabort wasn't breaking their applications unless it was highlighted better.

The first way would have been much better. However, the change to cfabort is similar to what they did with listToArray() and empty list items, and they didn't preserve backwards compatibility by default for that either, so I'm not really surprised. I am surprised they didn't provide a way in the tag or the statement (for script syntax) a way to preserve backwards compatibility.

But it doesn't really change my opinion that they were right to go this route (even if their implementation of the change was flawed).

@Adam,
My position is that cfabort should not do anything to the request, but should abort the page processing. I consider the page processing to be separate from the request.

Looking at the livedocs, Adobe uses different terminology when decribing how the request event handlers function vs cfabort. They don't say cfabort should abort the request, they say it "Stops the processing of a ColdFusion page at the tag location." The cfexit tag doesn't really apply either since according to the livedocs that's to end execution of custom tag processing.

This is why in my previous comment I mentioned that the bug comes from the idea that page processing != request processing. I think the difference in opinion comes down to a difference in interpretation of what is in the documentation. There isn't anywhere I've seen where they've explicitly related the two or not.


Jun 29, 2011 at 5:35 PM // reply »
49 Comments

The documentation is wrong - prior to CF9, aborting the request is exactly what it did (as proven by it not executing onRequestEnd method nor OnRequestEnd.cfm template).

Documentation should always be corrected to match actual behaviour on a released product, (even if the plan is to then change that behaviour in the next release).

I'm not sure what you're referring to with ListToArray - they added a new argument with CF8 and another one with CF9, but in both cases the default behaviour has not changed. Empty list items have always been ignored, and still are (assuming the docs are not incorrect here).

Dan: We've already got onError and cfthrow to do that?

The way I would likely have solved this problem is to add an onFinally method that would always run last, whether the request ended with onRequestEnd, onError, cfabort, or any other way of stopping processing that doesn't go down these channels. The first argument to that method would be which route it went down, and the remaining arguments would be the parameters sent into the previous method (or the attributes to cfabort if that was how it ended).

But I think the key thing which is annoying/perplexing me and Adam (and others) is that Adobe silently changed behaviour without (afaik) telling anyone - if it wasn't for David & Ben's discovery, we'd still be unaware - and if anyone had asked "does onRequestEnd run after cfabort?" I'd have responded with a firm no, because it's something I've explicitly tested in the past and the type of change that shouldn't happen without notification.


Jun 29, 2011 at 5:42 PM // reply »
5 Comments

Hi again Mike
Clearly the intent of CFABORT, irrespective of the wording of the docs, is to abort the REQUEST, not the "page" (I wish Adobe et al would stop referring to a CF file as a "page". It sounds like something someone who would say "the internets" would say. But anyway). If CFABORT terminated processing of the CFM file it was in, then if that file was included by a "parent" file, one would expect processing to resume in the parent file after the CFINCLUDE line. Like how CFEXIT does. Indeed that they actually have CFEXIT for this kinda demonstrates that is specifically NOT what CFABORT is for.

BTW, the docs for CFEXIT are wrong. Well: they're right in the context of custom tag execution, but they're wrong when it comes to how it works within the "normal" execution flow of CF files. CFEXIT stops execution of the current *file*, and skips out to the file that called that one. If the CFEXIT is in the "outermost" file, then it exits and that's the end of processing. Other than... cough... onRequestEnd() also processing. But, TBH, I think that's the correct thing to do in this case.

Thinking more about this CFABORT / CFEXIT thing... the original change was unnecessary as we've already got CFEXIT to effect the intent of the original enhancement request.

--
Adam


Jun 29, 2011 at 6:00 PM // reply »
49 Comments

Adam, the concepts of Request, Page, and Template are actually three distinct things.

Request I think we can agree as being the moment CF gets tapped on the shoulder by the web server, up until it delivers its response and waves goodbye.

And Template is just a way of saying "file", or possibly "file that isn't a cfc", depending how you see it.

A Page is kinda of a wrapper for a Template which comes from the JEE side of CF:
http://download.oracle.com/javaee/1.4/api/javax/servlet/jsp/PageContext.html

Ah, here we go, the comments to one of Ben's previous entries describes it in a nice way:
http://www.bennadel.com/blog/919-GetPageContext-Is-Template-Specific-Not-Request-Specific.htm

As for cfexit already being a solution - well, if you have a series of included pages, there's no way to break the execution of all of them without messy logic in each one, so a cfexit method="exitRequest" which immediately stops and calls onRequestEnd would be a sensible enhancement.


Jun 29, 2011 at 6:15 PM // reply »
5 Comments

Hi Peter:
I'm not so sure about the JSP analogy you make there. Obviously it's absolutely on the button now that CF runs atop of Java, but CF's usage of the term "page" predates this. It's short for "web page" in the CF usage.

It was perhaps relevant back when the approach to making CF-driven websites was to have one CFM file per web page "template" (hence the term "template" in CF parlance too), but it's not relevant these days now that that's not really the (recommended) approach CF uses.

If anything the JSP terminology is a hang-over from that too. Not CF's specific usage, but of web page construction in general "back then".

But that's a digression.

You're spot on about the CFEXIT thing though. I didn't think of that. However it does demonstrate that the requirement would be more easily solved by enhancing CFEXIT, not monkeying with CFABORT.

--
Adam


Jul 4, 2011 at 5:11 AM // reply »
31 Comments

Out of curiosity, I'm curious if there are any other tags that this has affected. The only other one that springs to mind would be CFContent when used in certain ways. Doesn't that stop the page processing when it's done?


Jul 4, 2011 at 5:16 AM // reply »
25 Comments

Hi David:
Why don't you try it and find out! (And then maybe report back...)

--
Adam


Jul 13, 2011 at 9:16 PM // reply »
11,246 Comments

@Adam,

Thanks for the bug submission :)

@Dan,

Along those lines, what could be interesting is an onAbort() event handler in the Application.cfc. Then, the programmer could have the option to turn around and invoke the onRequestEnd() event handler if they wanted to.

It's somewhat like when we (speaking generally) invoke onApplicationStart() manually from the onRequestStart() event handler if the app needs to be reset in someway based on a trigger.

@David,

So far, the one's I've tested are just CFLocation and CFAbort. In the past, I have seen that CFContent does stop processing in the *same* file:

http://www.bennadel.com/blog/1939-Using-CFContent-s-Variable-Attribute-Stops-All-Further-Processing.htm

... of course, none of these are about the same file - they are about the onRequestEnd() event handler. I can run a test on this; now I'm curious.


Jul 14, 2011 at 3:40 AM // reply »
25 Comments

Hi Ben
It's good to see that issue accumulating votes. I dunno whether Adobe actually pay attention to these things, but at least as a community, we're doing our bit to improve CF.

Now... I was thinking about the mention of CFCONTENT on this thread. The more I think about it, the less I'm inclined to think there's a good reason to expect CFCONTENT to NOT fire onRequestEnd().

I'd be keen to hear why people think it shouldn't.

--
Adam


Jul 14, 2011 at 9:01 AM // reply »
11,246 Comments

@Adam,

I'll agree with you on CFContent. I think CFAbort is really the only one that I think feels completely wrong.


Nov 10, 2011 at 7:07 PM // reply »
3 Comments

If CFABORT behavior does not get rolled back to pre-9, perhaps we need a new tag? Something like CFDEAD, CFKILL, CFDONE, CFEND, CFTERMINATE or my favorite CFSTOP...???


Ike
Apr 21, 2012 at 8:34 PM // reply »
1 Comments

Hey Ben! Thanks for posting this. I was struggling with an issue I was sure was a bug in CF9 where I would get a dump of a request variable and then on the very same line as the CFDUMP tag, I would get an error saying that the variable wasn't defined in the request. It turned out actually to be this change of behavior.

I had previously arranged my framework to do basically the same thing (for reasons I won't get into right now ;P) and so now the onRequestEnd was executing twice. Executing twice normally wouldn't be such a great thing anyway, but I had also cleared the request scope at the end of the event to help resolve a previous issue with memory leaks in an earlier version of CF. D'oh! So that was causing the weird "here it's NOT" error messages.

The whole process of debugging it is on my blog here: http://ontap.riaforge.org/blog/index.cfm?mode=entry&entry=58993158-CFC0-A2D4-EB631D225F064EDD


Apr 5, 2013 at 4:59 PM // reply »
17 Comments

Does anyone ave recent bug base links from the above comment thread? Now, that the Flex version of the bugbase isn't used, all those links are dead, and it's bloody impossible to search the new bugbase for the original bug id.


Apr 5, 2013 at 8:40 PM // reply »
8 Comments

Hi Brad / Ben
The URL on the new tracker is this: https://bugbase.adobe.com/index.cfm?event=bug&id=3040459

I slapped together what I think is a better search UI for the new bug tracker, and used that to find it:
http://adamcameroncoldfusion.cfmldeveloper.com/cfbugs/search.html

Note there's a slight bug in that one needs to clicke the "Search" button instead of just pressing enter (which just errors). I've yet to get around to fixing that.

Anyway, HTH.

--
Adam



Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 23, 2013 at 9:52 PM
Preventing Links In Standalone iPhone Applications From Opening In Mobile Safari
@Muhmmadibn Did you figure out a solution to launching PDFs? I am running into the same issues myself. There is no way to close the PDF or go back once you launch it. Thanks in advance! ... read »
May 23, 2013 at 6:06 PM
The Girl Who Broke My Heart, And Made Me A Better Person
Good day,ladies and gentle men, my name is Dr AMADI the great spell caster in Africa, i have help so many people for different kind of problems,who say there is no solution to problems on earth, that ... read »
May 23, 2013 at 4:26 PM
ColdFusion QueryAppend( qOne, qTwo )
@Heather, Glad people are still getting value out of this! ... read »
May 23, 2013 at 3:49 PM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@WebManWalking, I meant the code at the bottom (not the video). I did try to experiment with an intermediary variable, like: value = users.id[ i ]; arrayContains( userIDs, value ); ... but t ... read »
May 23, 2013 at 11:06 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, Are you talking about As Number: YES As String: YES As Java: YES? If so, that's with 3 different ways of referencing the constant 1, not users.id[1]. Query object references(*) are what seem ... read »
May 23, 2013 at 9:55 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Dan, According to the CF Admin, I'm running Java "1.6.0_45". As far as the DB column, in the database it's an INT. I'll see if I can dig into what CF sees it as. @WebManWalking, But h ... read »
May 23, 2013 at 9:49 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, I think the problem is that we're used to loose typing in ColdFusion, like JavaScript. If a value is a number but it's needed in an expression to be a string, noooo problem. I've encountered ... read »
May 23, 2013 at 9:47 AM
ColdFusion QueryAppend( qOne, qTwo )
You rock! Thank you, thank you, thank you!!! ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools