Flash Player feature request
We are working on an OSGI framework implement in AsctionScript 3 FP9+ that can be used for both Flex and Flash combined if needed.
As you may know one of the key features of an OSGI framework is the way it defines bundles interoperability using services and service consumers.
Some bundles may export services that other bundles will consume. The framework offers means of subscribing and unsubscribing from services, the service consumers than cast the framework provided object to the needed ServiceInterface. This way strong typing and code hinting is available during bundle design and the dependency to any particular ServiceImpl is removed.
There are 2 ways that I found that this could be implemented, but both ways have major drawbacks.
Solution 1: The first thought would be to load every bundle in it’s own ApplicationDomain this would make sure no name space collusions will occur and no other bundle will be able to access it’s content directly (not impossible but rather difficult).
Scenario: BundleA export a service using the IService interfae and BundleB consumes this service, both Bundles are compiled with the Interfaces build in.
Drawback: when BundleB code will cast the Framework.getService(IService) as IService … it will obviously get null, since the IService interface in BundleA and BundleB are of 2 different versions. Even though their code is identical VM considers them 2 distinct definitions since they are in 2 distinct ApplicationDomains. If the BundleB will not cast it to IService it will be able to call methods and access public properties, but no compile time checking and generally error-prone.
Also Only objects available into the parent application domain will be accessible using their names everything else is just generic Object.
Solution 2: Another way to implement this would be to load everything into the main ApplicatonDomain, which will solve the above drawback but will add others.
Scenario: BundleA uses a class called HashMap version 1, Bundle B uses the same class HashMap but version 2, Version 2 is backwards compatible with Version 1, but not the other way around.
Drawback: if the first to load will be BundleA then it will register the HashMap v1, and when BundleB loads it’s v2 of HashMap will be ignored so BundleB will most probably misbehave, throw exceptions etc. and this errors are quite difficult do debug.
A way around Solution 1’s Drawback would be to export the interfaces to a separate SWF file and load it into the main ApplicationDomain, Interface collisions are less likely to occur in a flash application (relatively low complexity compared to potential server-side complexity). But this requires lots of configuration to be done during compile time, as the interfaces need to be explicitly excluded from 1 SWF and Included in another, and it still not solves all of the potential problems.
One of the reasons we cannot find a way around them is the ApplicationDomain beeing final. Although we understand the security reasons for it being final, we still think it’s a bit too restrictive. There should be a mechanism for one ApplicationDomain to import definitions from another ApplicationDomain if the SWF loaded into that ApplicationDomain exports those definitions.
Also it would be very good if one could define a ClassLoader for an ApplicationDomain so if a var bundleAvar = new BundleBClass() is called and BundleBClass is missing in BundleA … the BundleA.applicationDomain.
in this case if the IWeatherService is not defined in the Bundle (and it should not be defined) .. it can be loaded from the bundle that provides the service and has marked the Interface as exported.
To sum it up, what I suggest is to add 3 methods(getter/setter) to the ApplicationDomain:
- get/set definitionResolver():IDefinitionResolver, can only be set from within the ApplicationDomain, most secure way would probably be to set it at compile time and deny override at runtime.
- exportPackage or exportDefinition(definition:Class) … callable only from within the current ApplicationDomain;
- importPackage or importDefinition(QualifiedClassName:String, provider:ApplicationDomain) … callable for the current applicationDomain
the IDefinitionResolver should have:
- resolve(QualifiedClassName):* or throws the Definition Not found exception