Biased Flash Series by AppleInsider

This is old news now, but it’s worth noting that AppleInsider released a 3-part series of articles about Flash. Check them out here:

Whether you love Flash, hate Flash, love Adobe, hate Adobe, or are neutral with the whole thing, this series is disturbingly prejudicial and biased. Comments on the posts even say as much. This is certainly disappointing, because I like AppleInsider, Apple, Adobe, and Flash. But these articles have a predisposition that results in negative interjected commentary in what would otherwise have been an interesting consideration of the history of Flash.

Memento Pattern is Great for Web Services

Nothing Earth-shattering here. I've just come to appreciate how useful the memento design pattern can be to do web services a bit more efficiently sometimes.

My web service is a facade CFC powered by objects being generated by Transfer and ColdSpring. So I conveniently have access to the objects' getMemento() or getPropertyMemento() methods.

I had a method in the web service that would accept several arguments and perform the simple task of processing them and saving them to the database. It returned a single boolean indicating success or failure.

The web service is just used by another web server that doesn't have access to the database and can't run Transfer (because it is currently running CFMX 6). This web server hosts a page with a form; when the user submits the form, the action page invokes the web service to process and save the information.

I needed the action page to display some data that wasn't available in the form data that was transmitted. For instance, a pull-down menu allowed the user to select from a list of sessions, but all that is transmitted is the database ID of the session they chose. My action page can't display any details about that session unless it makes another web service call to retrieve the details of that session. And I have to write the code for that web service method, which up to this point was unnecessary.

Aha! I changed the web service to return a struct that still contained the success/failure boolean, but also contained mementos of the objects that were created or referenced when processing the request. Now my web server invoking the web service has all the details of the session at its disposal for display on the action page.

What's the big deal, right? Who cares about one more web service call? Well, I'd like to keep the chatter between the two machines down to a minimally required amount, and besides, thanks to getMemento(), adding all of that data to the web service's response took one line of code, and way less time than it would take to write that extra web service method to retrieve session data, and also way less time than it took to even write this blog post. ;-)

Here's the general thought process of the code in the web service (abbreviated to just show the point of mementos):

<cffunction name="add" access="remote" returntype="struct">
<cfscript>
  var ret=StructNew();
  var mySession=SessionService.getSession(Arguments.SID);
  /* Do Processing */
  ret.Success=true;
  ret.Session=mySession.getMemento();
</cfscript>
<cfreturn ret>
</cffunction>

Using SQL to Retrieve SQL

At work, someone made a request that required me to look through potentially hundreds of views in dozens of databases on our SQL Server. I certainly didn't want to examine each one at a time. How could I speed up this process with code?

Well, you can find all of your views by querying the sysobjects table, and you can retrieve the SQL behind the views by querying the syscomments table. Something like this works well:

SELECT RTrim(sysobjects.name) AS ViewName,
       RTrim(syscomments.text) AS ViewSQL
FROM   sysobjects JOIN syscomments
ON     syscomments.id=sysobjects.id
WHERE  sysobjects.xtype='V' AND sysobjects.category=0

This will retrieve the SQL code and names of all the views in the current database. This simple query is the heart of the solution. But I would like to retrieve this information for all of the databases.

Well, it's easy enough to get a list of all the databases. The sysdatabases table in the master database has that list. You can query that table, perhaps filtering out some of the system or sample databases included with SQL Server:

SELECT name FROM master.dbo.sysdatabases
WHERE name NOT IN ('tempdb','master','msdb','pubs','model')
ORDER BY name

Now just combine this information. To accomplish this, we'll build a stored procedure that will create a temporary table, loop through the databases and query each one for its views, insert the view information into the temporary table, and return the temporary table.

Something like this will do the trick:

CREATE PROC dbo.selectViews   AS
BEGIN

-- Vars
DECLARE @dbname sysname

-- Temp Table
CREATE TABLE #Results
(  
  DatabaseName varchar(200),
  ViewName varchar(200),
  ViewText nvarchar(4000)
)

-- Loop Thru the Databases.
DECLARE dbnames_cursor CURSOR
FOR
  SELECT name FROM master.dbo.sysdatabases
  WHERE name NOT IN ('tempdb','master','msdb','pubs','model')
  ORDER BY name
OPEN dbnames_cursor
FETCH NEXT FROM dbnames_cursor INTO @dbname
WHILE (@@FETCH_STATUS &lt;&gt; -1)
BEGIN
  IF (@@FETCH_STATUS &lt;&gt; -2)
  BEGIN   
    -- Grab the Views of this Database and Put them in the Temp Table.
    SET @dbname = RTRIM(@dbname)
    INSERT INTO #Results
    EXECUTE
    (
      'SELECT '''+@dbName+''' as DatabaseName, ' +
      'RTrim(' + @dbname + '.dbo.sysobjects.name) as ViewName, ' +
      'RTrim(' + @dbname + '.dbo.syscomments.text) as ViewText ' +
      'FROM ' + @dbName + '.dbo.sysobjects join ' + @dbName + '.dbo.syscomments ' +
      'ON ' + @dbName + '.dbo.syscomments.id=' + @dbName + '.dbo.sysobjects.id ' +
      'WHERE ' + @dbname + '.dbo.sysobjects.xtype=''V'' and ' + @dbname + '.dbo.sysobjects.category=0 '
    )
  END
  FETCH NEXT FROM dbnames_cursor INTO @dbname
END
CLOSE dbnames_cursor
DEALLOCATE dbnames_cursor
SELECT * FROM #Results order by DatabaseName, ViewName
DROP TABLE #Results

END
--

Now just execute the stored procedure and review its output.

exec selectViews

This portion of the solution just retrieves the data. After writing this, I developed a really short and simple ColdFusion application that would output the database name, view name, and SQL to a table, and used some simple JavaScript to make it easier to search and filter the views. The client-side methods used to view and work with the data are obviously up to you.

Note for Flex Newbie: Test Your HTML Wrapper

Note to self (the Flex newbie): Test your HTML wrapper for your SWF files to make sure that the user experience is a good one when you're setting up your Flex apps on your site! I took the HTML wrapper generated by Flex Builder and modified it to fit into my page. While doing so, I forgot to include the playerProductInstall.swf file with my app, so when a browser with an older Flash Player viewed the page, the JavaScript I had in place to call playerProductInstall.swf would hang since it couldn't find it. The detection for when no Flash Player is present at all (or only a really old version) was also a bit ungraceful.

But I was clueless to these poor experiences since I hadn't tested these scenarios. Fortunately, my app was just released, and only to a beta crowd.

To test the absence of Flash Player, you can download a Flash Player Uninstaller and uninstall Flash Player. I'm not sure what the best way is to install and test an old version of the Flash Player; I just had a virtual machine that had an old version on it, so I used that because it was convenient. If you google for "download flash player 8", there are some non-Adobe links that appear to be valid links, but I did not try these.

Adobe has a Flash Player Detection and Installation support page that is helpful as well.

  Theme Brought to you by Directory Journal and Elegant Directory.