Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I have a ActionScript 3 AIR class that I'd like you to check for robustness. I know it's quite a lot of code and It's not required that you review the full code, but maybe you see general problems in the way I handle exceptions or maybe you spot something that could make troubles.

The purpose of the Class is to load a local SWF and to run it within the AIR security sandbox. Before doing so, it might update the swf in two ways:

  1. either there is a MyApp_update.swf present in the app-storage directory - in this case it should simply replace the current swf by this swf.

  2. if that's not the case, it connects to a server and checks whether a new version is available by downloading a meta-info.xml and comparing the version number on the server with the version number of the local meta-info.xml

It's important that this part of the code is robust, since the rest of the application we can easily update remotely (in case this class here is doing it's job well).

package
{

    import flash.desktop.NativeApplication;
    import flash.display.Loader;
    import flash.display.NativeWindow;
    import flash.display.NativeWindowInitOptions;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import flash.events.UncaughtErrorEvent;
    import flash.filesystem.File;
    import flash.filesystem.FileMode;
    import flash.filesystem.FileStream;
    import flash.net.URLLoader;
    import flash.net.URLLoaderDataFormat;
    import flash.net.URLRequest;
    import flash.system.ApplicationDomain;
    import flash.system.LoaderContext;
    import flash.utils.ByteArray;


    public class MyApp_Base extends Sprite
    {
        private var _remoteMetaRequest:URLRequest = new URLRequest("http://www.myServer.com/updater_test/my_app_meta.xml")
        private var _remoteSwfRequest:URLRequest = new URLRequest("http://www.myServer.com/updater_test/MyApp.swf");

        private var _localSwfFile:File = new File("app-storage:/application/MyApp.swf");
        private var _localSwfUpdateFile:File = new File("app-storage:/application/MyApp_update.swf");
        private var _localMetaFile:File = new File("app-storage:/application/my_app_meta.xml");
        private var _localMetaXML:XML;
        private var _remoteMetaXMLString:String;
        private var _remoteMetaXML:XML;

        private var _swfLoader:Loader = new Loader();
        private var _swfUrlLoader:URLLoader = new URLLoader();
        private var _progressBox:LoaderBox = new LoaderBox();
        private var _progressWindow:NativeWindow;

        public function MyApp_Base()
        {
            loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, onUncaughtError);

            //check if a local swf update file is present. if so, replace the current version with it, delete the update file and skip checking the server
            if(_localSwfUpdateFile.exists){
                _swfUrlLoader.dataFormat = URLLoaderDataFormat.BINARY;
                _swfUrlLoader.addEventListener(Event.COMPLETE, updateSwfLoaded);
                _swfUrlLoader.addEventListener(IOErrorEvent.IO_ERROR, localUpdateError);

                try{
                    _swfUrlLoader.load(new URLRequest(_localSwfUpdateFile.nativePath));
                }
                catch(e:Error){
                    showError("Error", "Error 1 while loading local update file. If this error persists, please contact the application author. \n"+e.message);
                    return;
                }
            }
            else{ //check the server for a new update
                //load the local Metadata file:
                var s:FileStream = new FileStream();
                try{
                    s.open(_localMetaFile,FileMode.READ);
                    var xmlString:String = s.readUTFBytes(s.bytesAvailable);
                    _localMetaXML = new XML(xmlString);
                    s.close();
                }
                catch(e:Error){
                    showWarning("Warning", "There was an Error while loading the file " +_localMetaFile.nativePath +". This could mean that your installation is corrupted. The updateserver is not checked for updates: \n"+e.message);
                    loadAndRunLocalSwf();
                    return;
                }

                //load the remote Metadata file:
                var urlLoader:URLLoader = new URLLoader();
                urlLoader.addEventListener(Event.COMPLETE, remoteXMLLoaded);
                urlLoader.addEventListener(IOErrorEvent.IO_ERROR, remoteXMLError);
                try{
                    urlLoader.load(_remoteMetaRequest);
                }
                catch(e:Error){
                    showWarning("Warning", "There was an Error while loading the file " +_remoteMetaRequest.url +". This error does not affect application execution, but prevents the application from being updated. If this error persists, please contact the application author. \n"+e.message);
                    loadAndRunLocalSwf();
                    return;
                }
            }
        }

        private function remoteXMLLoaded(e:Event):void{
            if(e.target.bytesLoaded!=e.target.bytesTotal){
                trace("internet connection got interrupted during download of remoteXML");
                remoteXMLError(null);
                return;
            }

            try{
                _remoteMetaXMLString = e.target.data;
                _remoteMetaXML = new XML(_remoteMetaXMLString);
                var localVersion:int = _localMetaXML.version;
                var remoteVersion:int = _remoteMetaXML.version;
            }
            catch(e:Error){
                    showWarning("Warning", "The file " +_remoteMetaRequest.url +" is not correctly formatted. This error does not affect application execution, but prevents the application from being updated. If this error persists, please contact the application author. \n"+e.message);
                    loadAndRunLocalSwf();
                    return;     
            }

            if(remoteVersion>localVersion)
                updateSWF();
            else
                loadAndRunLocalSwf();
        }

        private function remoteXMLError(e:Event):void{
            loadAndRunLocalSwf();
        }


        private function updateSWF():void{
            _swfUrlLoader.dataFormat = URLLoaderDataFormat.BINARY;
            _swfUrlLoader.addEventListener(Event.COMPLETE, updateSwfLoaded);
            _swfUrlLoader.addEventListener(IOErrorEvent.IO_ERROR, remoteSwfError);
            _swfUrlLoader.addEventListener(ProgressEvent.PROGRESS, onProgressDownload);

            showProgressWindow("Downloading important software update");

            try{
                _swfUrlLoader.load(_remoteSwfRequest);
            }
            catch(e:Error){
                showWarning("Warning", "There was an Error while loading the file " +_remoteSwfRequest.url +". This error does not affect application execution, but prevents the application from being updated. If this error persists, please contact the application author. \n"+e.message);
                loadAndRunLocalSwf();
                return;
            }
        }

        private function remoteSwfError(e:Event):void{
            if(_progressWindow){
                try{
                    _swfUrlLoader.close();
                    _progressWindow.close();
                    _progressWindow = null;
                }
                catch(e:Error){}
            }
            loadAndRunLocalSwf();
        }

        private function localUpdateError(e:Event):void{
            showError("Error", "Error 2 while loading local update file. If this error persists, please contact the application author. \n"+e.toString);
        }

        private function updateSwfLoaded(e:Event):void{
            if(e.target.bytesLoaded!=e.target.bytesTotal){
                trace("internet connection got interrupted while downloading remote MyApp.swf");
                remoteSwfError(null);
                return;
            }
            if(_progressWindow)
                _progressWindow.close();
            _progressWindow = null;

            var ba:ByteArray = e.target.data;

            //if old backupfile exists, delete it
            var backup:File = new File("app-storage:/application/MyApp_backup.swf");
            if(backup.exists){
                try{
                    backup.deleteFile();
                }
                catch(e:Error){
                    showError("Error", "Error while deleting file "+backup.nativePath+" : \n"+e.message);
                    return;
                }
            }

            //make current SWF the backup
            var appFile:File = new File("app-storage:/application/MyApp.swf");
            try{
                appFile.moveTo(appFile.parent.resolvePath("MyApp_backup.swf"));
            }
            catch(e:Error){
                showError("Error", "Error while creating backup of "+ appFile.nativePath+" . Make sure that there is only one instance of the MyApp running. If this error persists, please contact the application author. \n"+e.message);
                return;
            }

            //write the downloaded SWF to the disk
            var s:FileStream = new FileStream();
            try{
                appFile = new File("app-storage:/application/MyApp.swf");
                s.open(appFile, FileMode.WRITE);
                s.writeBytes(ba);
                s.close();
            }
            catch(e:Error){
                showError("Error", "Error while writing MyApp.swf to disk. Will try to restore backup. If this error persists, please contact the application author. \n"+e.message);
                backup = new File("app-storage:/application/MyApp_backup.swf");
                backup.moveTo(backup.parent.resolvePath("MyApp.swf"));
                return;
            }

            if(_localSwfUpdateFile.exists){//the update was performed by a local update file
                try{
                    _localSwfUpdateFile.deleteFile();
                }
                catch(e:Error){
                    showError("Error", "Could not delete " + _localSwfUpdateFile.nativePath + ". If this error persists, please contact the application author. \n"+e.message);
                    return;
                }
            }
            else{   
                //write the downloaded MetaData to the disk
                try{
                    s.open(_localMetaFile,FileMode.WRITE);
                    s.writeUTFBytes(_remoteMetaXMLString);
                    s.close();
                }
                catch(e:Error){
                    showError("Error", "Error while writing "+ _localMetaFile.nativePath+" to disk. Make sure that there is only one instance of the MyApp running. If this error persists, please contact the application author.\n"+e.message);
                    return;
                }
            }

            //run the loaded SWF
            loadAndRunLocalSwf();
        }

        private function loadAndRunLocalSwf():void{
            var context:LoaderContext = new LoaderContext();
            context.allowLoadBytesCodeExecution = true;
            context.applicationDomain = ApplicationDomain.currentDomain;

            var s:FileStream = new FileStream();
            try{
                s.open(_localSwfFile,FileMode.READ);
                var ba:ByteArray = new ByteArray();
                s.readBytes(ba);
                s.close();

                _swfLoader.loadBytes(ba,context);
            }
            catch(e:Error){
                showError("Error", "Error while launching "+_localSwfFile.nativePath+". This could mean that your installation is corrupted. If this error persists, please contact the application author.\n"+e.message);
                return;
            }
        }

        private function onUncaughtError(e:UncaughtErrorEvent):void
        {
            e.preventDefault();
            var error:Error = e.error;
            showWarning("Uncaught Error:", error.message);
            return;
        }

        private function showError(title:String, msg:String):void{
            var errorBox:ErrorBox = new ErrorBox(msg);

            var nativeWinOpt:NativeWindowInitOptions = new NativeWindowInitOptions();
            nativeWinOpt.maximizable = false;
            nativeWinOpt.minimizable = true;
            nativeWinOpt.resizable = false;

            var window:NativeWindow = new NativeWindow(nativeWinOpt);
            window.stage.scaleMode = StageScaleMode.NO_SCALE;
            window.stage.align = StageAlign.TOP_LEFT;
            window.width = errorBox.width;
            window.height = errorBox.height;
            window.addEventListener(Event.CLOSE, closeApplication);
            errorBox.addEventListener(Event.CLOSE, closeApplication);
            window.title = title;
            window.stage.addChild(errorBox);
            window.activate();
        }

        private function showWarning(title:String, msg:String):void{
            var warningBox:WarningBox = new WarningBox(msg);

            var nativeWinOpt:NativeWindowInitOptions = new NativeWindowInitOptions();
            nativeWinOpt.maximizable = false;
            nativeWinOpt.minimizable = true;
            nativeWinOpt.resizable = false;

            var window:NativeWindow = new NativeWindow(nativeWinOpt);
            window.stage.scaleMode = StageScaleMode.NO_SCALE;
            window.stage.align = StageAlign.TOP_LEFT;
            window.width = warningBox.width;
            window.height = warningBox.height;
            window.title = title;
            window.stage.addChild(warningBox);
            window.activate();
        }

        private function showProgressWindow(msg:String):void{
            _progressBox.setMessage(msg);
            _progressBox.setProgress(0.0);

            var nativeWinOpt:NativeWindowInitOptions = new NativeWindowInitOptions();
            nativeWinOpt.maximizable = false;
            nativeWinOpt.minimizable = true;
            nativeWinOpt.resizable = false;

            _progressWindow = new NativeWindow(nativeWinOpt);
            _progressWindow.stage.scaleMode = StageScaleMode.NO_SCALE;
            _progressWindow.stage.align = StageAlign.TOP_LEFT;
            _progressWindow.width = _progressBox.width;
            _progressWindow.height = _progressBox.height;
            _progressWindow.addEventListener(Event.CLOSE, cancelDownload);
            _progressBox.addEventListener(Event.CLOSE, cancelDownload);
            _progressWindow.title = "Downloading...";
            _progressWindow.stage.addChild(_progressBox);
            _progressWindow.activate();
        }

        private function onProgressDownload(e:ProgressEvent):void{
            _progressBox.setProgress(e.bytesLoaded/e.bytesTotal);
        }

        private function cancelDownload(e:Event):void{
            if(_progressWindow){
                try{
                    _swfUrlLoader.close();
                    _progressWindow.close();
                    _progressWindow = null;
                    loadAndRunLocalSwf();
                }
                catch(e:Error){}
            }
        }

        private function closeApplication(e:Event):void{
            NativeApplication.nativeApplication.exit();
        }
    }
}
share|improve this question

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Browse other questions tagged or ask your own question.