Background Primer:

Logging. As developers we all need to do this, especially for deployments where it’s entirely unreasonable to ask our end clients to use / maintain / notify us of crash events, and even if they did it gives us a needle’s-eye view of the problem. So while interim development usually starts off with good ole trace it’s potential for releasing sensitive data into the stream is greatly amplified by either the type of application you are writing and it’s complexity. Just having a debugger running, we’ve all seen every one else’s trace statements.

Security:

So AIR helps take the LogLogger from the SDK and gives us the potential to write log files, typically in the application storage area. While this can and does help the alpha and beta stages of release to end-clients it significantly increases their exposure of sensitive data because now you are writing it in area where only the user needs privilege – not admin.

The Better Approach:

Because we want to use the ILogger interface along with standard LogEvents (because Targets use these), the LogLogger is easy enough to re-write. With each message being ultimately broadcast in the event – this is the most appropriate domain to intercept, scramble, and resend. I started with each defined interface method and before each dispatch, a new method is used to build/construct the message. With the help of an implementation of a secure logger (composition goes a long way to help swap parts out instead of inheritance), this class is tasked with doing the actual work of rewriting or removing selected properties from objects we need to keep secure.

The Implementation:

The key with the build method is not only to introspect top level objects as part the …rest parameters, but also to see if anything is an array and to iterate that list to make sure the class type is or isn’t the type that needs to be secured. Any object which needs to be secured is then sent through the secure logger, otherwise a straight obj.toString() is used.

 

LogLogger

public class LogLogger extends EventDispatcher implements ILogger
{

private var _secureLogger:SecureLogger;
private var _category:String;
private var resourceManager:IResourceManager = ResourceManager.getInstance();

public function LogLogger(category:String)
{
super();
_category = category;
}

public function get category():String { return _category; }

public function log(level:int, msg:String, … rest):void
{
if (!hasEventListener(LogEvent.LOG)) return;
msg = build(msg, rest);
dispatchEvent(new LogEvent(msg, level));
}

public function debug(msg:String, … rest):void
{
if (!hasEventListener(LogEvent.LOG)) return;
msg = build(msg, rest);
dispatchEvent(new LogEvent(msg, LogEventLevel.DEBUG));
}

public function error(msg:String, … rest):void
{
if (!hasEventListener(LogEvent.LOG)) return;
msg = build(msg, rest);
dispatchEvent(new LogEvent(msg, LogEventLevel.ERROR));
}

public function fatal(msg:String, … rest):void
{
if (!hasEventListener(LogEvent.LOG)) return;
msg = build(msg, rest);
dispatchEvent(new LogEvent(msg, LogEventLevel.FATAL));
}

public function info(msg:String, … rest):void
{
if (!hasEventListener(LogEvent.LOG)) return;
msg = build(msg, rest);
dispatchEvent(new LogEvent(msg, LogEventLevel.INFO));
}

public function warn(msg:String, … rest):void
{
if (!hasEventListener(LogEvent.LOG)) return;
msg = build(msg, rest);
dispatchEvent(new LogEvent(msg, LogEventLevel.WARN));
}

private function get secureLogger():SecureLogger { return _secureLogger ||= new SecureLogger(); }

private function build(msg:String, params:Array):String
{
var repl:String =”;
for (var i:int = 0; i < params.length; i++)
{
var obj:Object = params[i];
if (!obj) repl = ‘null’;
else if (secureLogger.hasReplacementRule(obj))
{
if (obj is Array)
{
var n:int = (obj as Array).length;
for (var j:int = 0; j < n; j++)
{
var child:Object = obj[j];
repl += secureLogger.getReplacement(child);
}
}
else repl = secureLogger.getReplacement(obj);
}
else repl = obj.toString();

msg = msg.replace(new RegExp(“\\{“+i+”\\}”, “g”), repl);
}
return msg;
}
}

SecureLogger

public class SecureLogger
{
public function SecureLogger()  { super(); }

public function hasReplacementRule(obj:Object):Boolean
{
if (validateReplacementRule(obj)) return true;
if (obj is Array)
{
for each (var child:Object in obj)
{
if (validateReplacementRule(child)) return true;
}
}
return false;
}

private function validateReplacementRule(obj:Object):Boolean
{
if (obj is Account) return true;
if (obj is LoginCredentials) return true;
return false;
}

public function getReplacement(obj:Object):String
{
var str:String;
if (obj is Account) str = secureAccount(Account(obj));
else if (obj is LoginCredentials) str = ‘[LoginCredentials]‘;
else str = obj.toString(); return str;
}

//An example of using regex to re-write sensitive data
//Since it’s working with direct string manipulation, it must know about it’s format
private function secureAccount(obj:Account):String
{
var str:String = obj.toString();
str = str.replace(/number=\d+/, ‘number=’+ obj.id +’ ‘);
str = str.replace(/name=.*?(?=type)/, ‘name=Account ‘+ obj.id +’ ‘);
str = str.replace(/institutionName=.*?(?=institutionWebsite)/, ‘institutionName= Institution ‘+ obj.id +’ ‘);
str = str.replace(/institutionWebsite=.*?(?=status)/, ‘institutionWebsite= ‘); return str;
}

}

The entire OLAP package in Flex has sparse API documentation, but there are several ‘how-to’ examples out in the land of live-docs. This usually doesn’t bode well, because it generally means a tech-writer never talked to the development team to figure out what all the dependencies are on the various properties. We have the source, but chasing it down ourselves sucks.

 

A precursor into what doesn’t work:

  • OLAPAttribute.displayName does NOT work on subsequent dimensions. It will only work on your first dimension which is usually the ROW_AXIS. However, the OLAPLevel.attributeName will always expect to look up the OLAPAttribute.name.
  • The allMemberName even though it’s defined on the OLAPHierarchy, it must be actually set on an OLAPAttribute which is displayed for that dimension.

Now onto the real problem:

Generally to sort your cube, you do a 2-dimensional sort to the original flat dataprovider then assign this to the cube dataprovider.  And in most cases this seems to work OK, until your CATEGORY_AXIS is based on anything to do with Time (which is a lot of the time actually). In most cases you’ll have a two-tier hierarchy of year and month / quarter, but the problem appears to be most prevalent with months. Even though your original source is sorted correctly, some months will be placed out of order, especially if you’re using the allMember level which it builds on the fly in the query.

The Fix:

So the fix involves re-arranging the member IList in building the OLAPQuery to the appropriate OLAPSet. In addition to fixing the sort issue, now is the time to re-arrange the allMember level to where it seems logical for your application. Accounting applications generally read left-to-right, so summary total’s are generally shown on the right hand side.

The way know if you’re working with the allMember level that it created for you in the sort method, is to look for the OLAPMember.hierarchy.allMemberName and compare that to the current OLAPMember.name

The way to correctly resort months involves a bit of kludgery but I haven’t found a more elegant way around it. In the OLAPAttribute for the dimension you specify the dataField to the integer of the month (generally this is string from a formatter), and assign a displayNameFunction to use a DateFormatter to turn this into something meaningful at the UI level. Even though the name of the OLAPMember is a string, it’s actually a string cast of the original value to the dataField (hence the actual integer value). You can then re-cast this back to an int to do a numeric sort (so you don’t end up with 0, 1, 10, 11, 2, 3, etc).

Now you might think an easier way to do this is to just use a SortField, however since you’re original Object is transformed to a flash_proxy and lost in the process relative to the OLAPGrid, the internal numeric sort method to Sort will throw an error that it can’t find the property that you originally wanted to sort on (eg month).

This should get your columns back in the order in which they were meant to be in.

private function getQuery(cube:IOLAPCube):IOLAPQuery
{
… //Setup the rest of the dimensions / sets to associate to the correct axis

var list:ArrayCollection = cube.findDimension(“timeDimension”).findAttribute(“Month”).members as ArrayCollection;
list.sort = new Sort();
list.sort.compareFunction = compareSortMonth;
list.refresh();

var moSet:OLAPSet = new OLAPSet();
moSet.addElements(list);

private function compareSortMonth(a:OLAPMember, b:OLAPMember, fields:Array = null):int
{
if (a.name == a.hierarchy.allMemberName) return 1;
else if (b.name == b.hierarchy.allMemberName) return -1;

var aMonth:int = parseInt(a.name);
var bMonth:int = parseInt(b.name);

if (aMonth < bMonth) return -1;
else if (bMonth < aMonth) return 1;
else return 0;
}

Look Ma! Adobe finally did it. Flash Player 11.4, AIR 3.4 multi-threading. Let the thread-locking games begin!

http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/system/Worker.html

I updated Quicken because I was forced to, and we all loathe Quicken in one fashion or another, generally those of us in UI seriously need a job there. It would take months if not years to re-do this thing right, but then again what is their incentive? It’s a cash cow, no updates / improvements / bug-fixes, but everyone has to keep pony’ing up every 3rd year.

So I came across this one, in using it. OMG, what a retarded warning, so much so it will just make an JR / BASIC / ENTRY-LEVEL / etc, programmer laugh, and the rest of us to just cringe.

This one has been bugging me, and a few other people who were trying to figure out ultimately what version of SQLite is embedded in AIR. Running a SQLStatement of: ‘SELECT sqlite_source_id()’ gives us this result: onSQLresult result key sqlite_source_id() value 2010-12-07 20:14:09 a586a4deeb25330037a49df295b36aaf624d0f45.

As per SQLite.org this method returns the code characteristic for which your embedded version has been built against. Using the canonical timeline viewer, going back a few pages now of (200 entries) we find this one:

http://www.sqlite.org/cgi/src/timeline?n=200&a=2010-12-07+07%3A57%3A50
And low-and-behold down near the bottom: 2010-12-07 20:14 [a586a4deeb] Version 3.7.4 (user: drh, tags: trunk, release, version-3.7.4)

 

So it turns out, that the modified version of AIR 3.1 SQLite is based on 3.7.4 – and the real question of foreign key support is at least there – I was able to test against it using a simple CREATE TABLE, but according to the documentation- this is parsed but not enforced. Lets hope that is now wrong too – since even Adobe Employee’s couldn’t come up with this one.

Not sure how well some of us actually read this, but there is 1 section in particular that I find to be fairly interesting, and another is well, lets just say hopefully they learned since AS3 quite frankly is used in mission critical software.

Type inference: Type declarations will only be necessary in certain strategic places. Everywhere else the compiler will automatically infer suitable type declarations and treat the whole program as statically typed, even though the programmer does not have to state any types. Thus the convenience of untyped programming can be approximated quite well, while providing the performance advantages of typed programming.

So two comments on this one, one trivial the other not so much:

  1. Trivial – A lot of code-hinting programs will probably need some rework on giving the correct intelli-sense, since usually with RegEx it would be fairly straight forward (look for the next whole word after the colon where it’s declared). If I understand this correctly, basically what Adobe is hinting at is that the type declaration is only needed at the original source of the declaration? For code editors, this now means going back up the symbolic link chain to find where the parameter / class variable was originally created – this is really no small feat, and I would imagine would cause some parsing concerns.
  2. Opinion – Since when amongst any serious fundamental programmer is untyped “convenient”? Every major development platform that I know of, ALL uses strongly typed. Java, the entire C family, heck even databases do this with affinity. Seriously, it’s not convenient except anywhere more than one class is used. Typed languages have equal weight in performance (no prototypical look up), but also on the human – after all we are the ones who write this stuff, and well documented, well written code is hinged upon knowing exactly what type / interface you are dealing with at the moment.

 

Compatibility: The next version of ActionScript will be an evolution of ActionScript 3, but in some instances may not be completely compatible with ActionScript 3. We expect that any migration from ActionScript 3 will be significantly less burdensome than the move from ActionScript 2 to ActionScript 3. Regardless, we are exploring options for tooling that would either ease or automate this transition.

Adobe, please for the love of code, please don’t make us go through that again.

 

And lastly, a note on the Flex roadmap which obviously is of keen interest to me.

RSLs for Apache-released versions of Flex:
Now that Flex is a community-driven project, it is no longer appropriate nor practical for Adobe to sign the resulting Apache Flex RSLs. This means that when using an Apache Flex release, framework RSLs will not be cached globally by Flash Player, but rather per domain in the web browser.

Per domain in the browser?! Are you freaking serious? What’s the point of relying on a framework that is essentially stable and publicly available, only to have every single domain and sub-domain have to deal with loading their own copy? Wow I hope Apache is able to sign this sort of thing, and has the capacity on its servers to handle the request load.

Lastly, Flash nor Flex is dead as many in our industry fret – HTML5 is just another shiny ball that so many chase after looking for the proverbial development “fountain of youth.” Like outsourcing, it won’t be all that it’s cracked up to be and live up to it’s over-hyped promises, but instead will have it’s place in the world under certain circumstances.

In some instances when JSON is the only available communication protocol, we all know full well about it’s limitations especially when it comes to preserving type or saving state. But what if you were in a closed-loop system where you could simply save what the front-end end would send? In a Flex environment this is still somewhat trivial since you can still use the [RemoteClass] metatag even without an RPC-based mediary like LCDS.

In Flex 4.5 using the Adobe extension to JSON the following will work. In 4.6+ it gets a little harder since the JSON class is now a top-level object – which makes overriding private / modifying protected and/or static methods nearly impossible.

Modifying the encoder entails when encountering complex types that are not just anonymous Objects, if that’s the case then we can prefix the fully qualified class name before the bracket, and do the reverse in the decoder.

package
{

import JSONPlusEncoder;
import JSONPlusDecoder;

public class JSONPlus
{

public static const FQCN_IDENTIFIER:String = ‘fqcn’;

public static function encode(o:Object):String
{
return new JSONPlusEncoder(o).getString();
}

public static function decode(o:String, strict:Boolean = true):*
{
return new JSONPlusDecoder(o, strict).getValue();
}

}
}

 

package
{
import JSONPlus;
import com.adobe.serialization.json.JSONEncoder;
import flash.utils.getQualifiedClassName;
import flash.utils.describeType;

/**
* Note this implementation depends on monkey patching Adobe’s
* implementation (changing access type to protected)
* – big surprise – yet again a public facing
* API riddled with private methods.
* I wish they would learn to use protected.
*
*/
public class JSONPlusEncoder extends JSONEncoder
{

public function JSONPlusEncoder(value:*)
{
super(value);
}

override protected function objectToString(o:Object):String
{
var classInfo:XML = describeType( o );
if ( classInfo.@name.toString() == “Object” ) return super.objectToString(o);

//strip off the last bracket and insert the fully quallfied class name
var str:String = super.objectToString(o);
str = str.substring(0, str.length – 1);
str += ‘,”‘+ JSONPlus.FQCN_IDENTIFIER +’”:”‘+ getQualifiedClassName(o) +’”}’;

return str;
}
}
}

 

package
{
import JSONPlus;
import JSONDecoder;

import flash.utils.getDefinitionByName;

/**
* Note this implementation depends on monkey patching Adobe’s
* implementation – big surprise – yet again a public facing
* API riddled with private methods.
* I wish they would learn to use protected.
*
*/
public class JSONPlusDecoder extends JSONDecoder
{
private static var map:Object = {};

public function JSONPlusDecoder(value:String, strict:Boolean)
{
super(value, strict);
}

override protected function parseObject():Object
{
var o:Object = super.parseObject();
if (!o.hasOwnProperty(‘fqcn’)) return o;

var className:String = o.fqcn;
var cls:Class = getDefinitionByName(o.fqcn) as Class;

var instance:* = new cls();
for (var key:String in o)
{
if (key !=JSONPlus.FQCN_IDENTIFIER) instance[key] = o[key];
}
return instance;
}
}
}

As Flex Architects / Team Leads can attest (at least those of us who do the proper preparation for a project), choosing an architecture to base your code upon isn’t an easy task since many good ones are out there, however each is like a relationship that leaves just a little off the table to be truly a desirable solution. However as with any framework worth it’s work, most of them are highly extensible without the need to modify the original to achieve the characteristics you need / want.

RobotLegs: Mediating sub-content to a Spark NavigatorContent pane.
Turns out there is a ‘bug’ and/or limitation when using the spark-based layout framework, and thankfully it’s a relatively easy one to over come. It has to do with the <s:NavigatorContent /> and how those children are built according to it’s creationPolicy as set by an appropriate container (TabNavigator, Accordion, and so on). Because the mediator is triggered on the FlexEvent.CREATION_COMPLETE event, it doesn’t take into consideration that while additional containers will be put on the stage, their containing children will not be instantiated since it’s part of the deferred process. This leaves the mediator in ‘limbo’ land since we obviously want to setup the user gestures for all the guts of the container (combos, buttons, data grids, etc). (more…)

Off topic here, but in the sense of the Adobe development world it’s not since the new rollout uses the Flash Platform. Seriously though, generating a wrapper isn’t that hard especially since the FB or FX tool generates one for you to start with, but really this sort of egregious issue gets all the way into production – past the developers, past QA, past UAT? Really? How sad.

Even more ridiculous is the IE version has a whole host of issues:

  • The sandbox issue stems from http to https communication is not explicitly stated in the crossdomain policy.
  • The same remote translation object is not properly encoded on the GET parameters.
  • The caught exception where it’s painfully obvious the developer doesn’t understand even the most basic String API with either substr(start, length) or substring(start, end) – the .xml is missing which is why it can’t find the file.

 

There is probably only a handful of things that are probably less understood at the SDK level as to how just exactly the Drag-Drop event sequence is generated, captured, and handled. First off, the UIComponent is the one where events can be registered but does absolutely nothing about dispatching or even ‘psuedo-dispatching’ through an intermediary.

 

Not In Here:

Case is point – we all know about the DragManager, and how you have to enable certain targets to allow the capture sequence to generate events for the final drop event, however it’s really the SystemManager that handles a bunch of marshalling for you – since it sits at the near root level it knows about MouseEvents for just about every object.

It’s this ferrying back and forth between the InterManagerRequest.DRAG_MANAGER_REQUEST event between the DragManager and ultimately passed through the Implementor (ie DragManagerImpl), and the SystemManager, is what creates the DragEvent to eventually be fired via the DragProxy. This is why the documentation says it is important to trigger the allowDragDrop method from within the DragEvent.ENTER event – because the target is then assigned directly to the proxy instance.

(more…)

Next Page »