By now, it should be clear that applications, modules, and libraries (albeit bootstrapped as applications) are simply different forms of packaging .swf files. Libraries assume the tightest coupling with the loading code, and that’s why they get preloaded (by the application’s code generated by the Flex compiler). Modules get loaded and unloaded on demand, because they are needed only conditionally and only temporarily. Applications are similar to modules, in that they get loaded and unloaded on demand. The important advantage of applications over modules (as units of modularization) is that applications are self-sufficient, which allows you to mix multiple application .swfs compiled against different versions of the Flex framework (Flex 3.1, Flex 3.2, Flex 4.0, and so on).
Let’s elaborate. As you already know, libraries get loaded into the
same domain as the application: ApplicationDomain.currentDomain
. Accordingly, to
avoid conflicts, a library has to be compiled against the same version of
the Flex framework as the enclosing application. With modules, you get to
choose between the same domain or a child domain (new
ApplicationDomain(ApplicationDomain.currentDomain)
), but even in
the latter case, the class search starts with the parent domain. Again, to
avoid conflicts, modules have to be compiled against the same version of
the Flex framework as the consuming application. When it comes to
applications, you still may use same-domain or child-domain techniques,
provided that the loading application and subapplication are compiled
against the same version of the Flex framework. What if you can’t
recompile the Flex 3.2 subapplication and you want to load it from the
Flex 4 main application? Then you need to load into the domain that is the
sibling of the main application domain (new
ApplicationDomain(null)
).
Sibling domains allow ultimate separation of classes; you absolutely have to load the sub into the sibling domain to support multiversioning. That said, you may want to indiscriminately use sibling domains even when multiversioning is not an issue. A typical use case for this is portals, when you have to integrate portlets, perhaps developed by a third party. In brief:
If you can compile from sources, make modules and load them into the same domain or a child domain.
If you are integrating compiled applications, use sibling domains.
To simplify the discussion, the following sections will use the term “portlet” instead of the subapplication and “portal” instead of the loading application.
To load and unload a portlet, you have to use SWFLoader
(unless you are into writing your
own loader). As you remember, SWFLoader
is a wrapper around flash.display.Loader
. As such, SWFLoader
exposes the loaderContext
property that controls the
application domain precisely, like it does it for Loader
. For instance, Example 7-31’s MXML illustrates
the loading of the RemoteApplication.swf portlet using the
default loaderContext
.
Example 7-31. Using SWFLoader with default LoaderContext
<mx:SWFLoader id="swfLoader" source="http://localhost:8080/RemoteSite/RemoteApplication.swf" />
Identical results can be achieved by Example 7-32’s script.
Example 7-32. Using SWFLoader with explicit LoaderContext
private function loadApplication():void { swfLoader.loaderContext = new LoaderContext( false, new ApplicationDomain(ApplicationDomain.currentDomain) ); swfLoader.source = "http://localhost:8080/RemoteSite/RemoteApplication.swf"; }
In both cases, the portlet’s classes get loaded in the child
domain of the portal, according to the default loaderContext
of a flash.display.Loader
. However, there is more
to loaderContext
than controlling the
application domain.
When a Flex application is loaded from a web domain, Flash Player, by default, assigns it a security sandbox. Applications coming from the different web domains get assigned different sandboxes. As an example, consider that the portal comes from http://localhost and loads the portlet from http://127.0.0.1. Unless you deviate from the default settings, these two applications will be assigned different sandboxes. Remember that class definitions get loaded into application domains and that application domains form a tree. There is one and only one tree per sandbox.
You can read more about sandboxes in the Flash documentation (Adobe often refers to them as security domains as well), but a few important points should be noted here:
You can indicate the sandbox preference in the constructor of
the LoaderContext
. For instance,
Example 7-33’s code
snippet results in loading classes into the current security
sandbox.
Although you can easily load portlets from other web domains into the current sandbox, there is no way you can programmatically load the portlet from the same web domain into the different sandbox. In other words, you can admit strangers into your family, but you can’t expel your kin. And the only way to load a portlet into a different sandbox is to host it in a different web domain or subdomain.
Assigning a different sandbox means a totally different tree of application domains.
To sum up, there are only four loaderContext
combinations that you can arrange either programmatically or via hosting
the portlet on the different subdomain:
Table 7-1 illustrates
how you can achieve a particular combination—DSDD, SSDD, SSCD, and SSSD
(in this order)—provided that the portal and the portlet are hosted by
the different web domains. You can explicitly use the
loaderContext
property or you can manipulate
loadForCompatibility
and
trustContent
.
Table 7-1. Loading portlets across web domains
Table 7-2 illustrates how the combination SSDD, SSCD, and SSSD can be achieved, provided that the portal and the portlet are located on the same web domain.
Table 7-2. Loading portlets from the same web domain
Some of these scenarios make more sense than the others. In
particular, the Same Sandbox Same Domain scenario is the one most prone
to class name clashing. To reiterate: duplicate loading of a class in
the tree of application domains is not possible. At the same time,
sub’s code can easily and perhaps inadvertently
modify static variables of the classes hosted by the parent application.
This relates to classes, such as mx.core.Application
and mx.messaging.config.ServerConfig
, for
instance, and their properties application
and xml
, respectively.
On the opposite end is the Different Sandbox Different Domain scenario. Here you have the ultimate class isolation, which supports multiversioning plus ultimate security (more on this a bit later), at the price of a not-so-seamless user experience. For instance, the pop ups and alerts of the portlet will appear centered and clipped relative to the portlet rather than the entire portal, as shown in Figure 7-13.
The remaining two scenarios are Same Sandbox Child Domain and Same Sandbox Different Domain. The latter should be considered the top choice for enterprise portals, as it supports multiversioning and delivers a seamless user experience. The simpler scenario, Same Sandbox Child Domain, is the one you’ll examine next. After that, you’ll investigate scenarios that provide multiversioning support.
Same Sandbox Child Domain is the default scenario when the
application and the subapplication are located in a single web domain.
Unless you tell SWFLoader
otherwise, portlet classes
get loaded into the child application domain. To see how this works,
start with a sample portlet, such as RegularApplication.mxml, in Example 7-34.
Example 7-34. RegularApplication.mxml—sample portlet
<?xml version="1.0"?> <!-- RegularApplication.mxml--> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" implements="IGreeting" backgroundColor="0xffeeff" xmlns:local="*" creationComplete="onCreationComplete()"> <mx:Script> <![CDATA[ import mx.events.DynamicEvent; import mx.controls.Alert; import events.RemoteEvent; [Bindable] private var command:String=""; [Bindable] public var greeting:String = ""; public function setGreeting(value:String):void { greeting = value; } public function getGreeting():String { return greeting; } private function onCreationComplete():void { Alert.show("Loaded application talks back..."); // While you may use systemManager["swfBridge"] in the DSDD and SSDD, // systemManager.loaderInfo.sharedEvents will work always var swfBridge:IEventDispatcher = systemManager.loaderInfo.sharedEvents; // Subscribe to command from the application swfBridge.addEventListener("command", onCommand,false,0,true ); // Notify the application that creation has completed var evt:RemoteEvent = new RemoteEvent("creationComplete"); evt.data = ". Loaded application reported createComplete!"; swfBridge.dispatchEvent(evt); } private function onCommand(event:Event):void { command = event["data"] as String; } ]]> </mx:Script> <mx:Panel title="Loaded Application - Google News {greeting}{command}." width="90%" height="90%"> <local:GoogleNews width="100%" height="100%"/> </mx:Panel> </mx:Application>
RegularApplication.mxml
implements the interface IGreeting
from Example 7-12. Under the SSCD scenario, a
portlet will see the definition of the IGreeting
loaded by the portal. Accordingly,
the portal will be able to cast the portlet to IGreeting
, as shown in Example 7-35 (you may compare
swfLoader.content
with moduleLoader.child
).
Example 7-35. Interface-based scripting of the portlet loaded into the child domain
public function modifyValue():void { var systemManager:SystemManager = SystemManager(swfLoader.content); var loadedApplication:IGreeting = systemManager.application as IGreeting; loadedApplication.setGreeting(" accessed from outside"); }
Similarly to the way you arranged event-based communication with
the modules, this portlet listens to and communicates with the loading
application via loaderInfo.sharedEvents
(Example 7-36).
Example 7-36. Event-based portlet-portal communication via sharedEvents
private function onCreationComplete():void { var swfBridge:IEventDispatcher = systemManager.loaderInfo.sharedEvents; // Subscribe to command from the application swfBridge.addEventListener("command", onCommand,false,0,true ); // Notify the application that creation has completed var evt:RemoteEvent = new RemoteEvent("creationComplete"); evt.data = ". Loaded application reported createComplete!"; swfBridge.dispatchEvent(evt); }
Make sure to deploy RegularApplication.mxml into an entirely dedicated BlazeDS or LCDS context. This example creates a combined Flex/Java LCDS/Web Tools Platform (WTP) project called RemoteSite, as shown in Figure 7-14. (Please see the Adobe documentation on how to create a combined Flex/Java project with LiveCycle Data Services and WTP.) Having a dedicated Flex/JEE project enables you to define destinations of the portlet without affecting a portal or another portlet application.
To the
RemoteSite/WebContent/WEB-INF/flex/proxy-config.xml
file of this project, you need to add the destination GoogleNews
, as shown in Example 7-37.
Example 7-37. GoogleNews proxy destination
<destination id="GoogleNews"> <properties> <url>http://news.google.com/?output=rss</url> </properties> </destination>
Example 7-38 presents the class GoogleNews
, a descendant of DataGrid
that encapsulates HTTPService
and displays Google News headlines
to the user. When you run the portlet, it should look like Figure 7-15.
Example 7-38. GoogleNews DataGrid
<?xml version="1.0" encoding="utf-8"?> <!-- GoogleNews.mxml --> <mx:DataGrid xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="news.send()" dataProvider="{news.lastResult.channel.item}" variableRowHeight="true"> <mx:columns> <mx:DataGridColumn headerText="Date" dataField="pubDate" /> <mx:DataGridColumn headerText="Title" dataField="title" wordWrap="true" /> </mx:columns> <mx:HTTPService id="news" useProxy="true" destination="GoogleNews" resultFormat="e4x" fault="onFault(event)" /> <mx:Script> <![CDATA[ import mx.rpc.events.*; private function onFault(event:FaultEvent):void { mx.controls.Alert.show( "Destination:" + event.currentTarget.destination + " " + "Fault code:" + event.fault.faultCode + " " + "Detail:" + event.fault.faultDetail, "News feed failure" ); } ]]> </mx:Script> </mx:DataGrid>
Finally, consider the sample portal, SameSandboxChildDomainDemo.mxml, in Example 7-39. We suggest you create a separate combined Flex/Java/WTP Eclipse project, as shown in Figure 7-16. To illustrate the cross-domain specifics, you can run the portal from http://localhost while loading the portlet from the different domain, http://127.0.0.1.
Example 7-39. SameSandboxChildDomainDemo application
<?xml version="1.0"?> <!-- SameSandboxChildDomainDemo.mxml --> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:local="*" > <mx:Script> <![CDATA[ import events.RemoteEvent; import mx.managers.SystemManager; private const APP_URL:String = "http://127.0.0.1:8080/RemoteSite/RegularApplication.swf"; public function modifyValue():void { // Casting to SystemManager and IGreeting is possible var systemManager:SystemManager = SystemManager(swfLoader.content); var loadedApplication:IGreeting = systemManager.application as IGreeting; loadedApplication.setGreeting(" accessed from outside"); } private function loadApplication():void { swfLoader.addEventListener("complete", onLoadComplete); swfLoader.source = APP_URL; } [Bindable] private var applicationLoaded:Boolean; private var sharedEventDispatcher:IEventDispatcher; private function onLoadComplete(event:Event):void { applicationLoaded = true; sharedEventDispatcher = swfLoader.content.loaderInfo.sharedEvents; sharedEventDispatcher.addEventListener( "creationComplete", onLoadedApplicationCreated ); } [Bindable] private var reply:String=""; // Casting to RemoteEvent is possible private function onLoadedApplicationCreated(event: RemoteEvent):void reply = event.data as String; var remoteEvent:RemoteEvent = new RemoteEvent("command"); remoteEvent.data = ". Two-way communication works!"; sharedEventDispatcher.dispatchEvent(remoteEvent); } ]]> </mx:Script> <mx:HBox> <mx:Button label="Load Application" click="loadApplication()" /> <mx:Button label="Modify Value" click="modifyValue();" enabled="{applicationLoaded}"/> </mx:HBox> <mx:Panel title="Yahoo News{reply}" width="100%" height="50%" id="panel"> <local:YahooNews width="100%" height="100%"/> </mx:Panel> <mx:SWFLoader id="swfLoader" width="100%" height="50%" trustContent="true"/> </mx:Application>
Notice the setting trustContent="true"
of the swfLoader
. This guarantees that despite
different web domains of the portal and portlet, class loading happens
into the same sandbox and, by default, to the child application
domain.
That said, you should stick to the golden Flash security rule that the .swf (of the portal) can access a resource (portlet) on the different web domain only when such domain holds a cross-domain policy file that expresses trust to the domain of the .swf. So make sure your root web application contains the file shown in Example 7-40.
Example 7-40. Policy file cross-domain.xml
<?xml version="1.0"?> <!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd"> <cross-domain-policy> <allow-access-from domain="*"/> </cross-domain-policy>
Make sure that you do not use this indiscriminating policy file in production. For more information on secure cross-domain communication in Flash Player, see http://www.adobe.com/devnet/flashplayer/articles/secure_swf_apps.html.
SameSandboxChildDomainDemo.mxml has its own
news grid—it displays Yahoo! News. (The code of
YahooNews
is identical to GoogleNews
from Example 7-37, except that it uses the
different destination, as presented in Example 7-41. You should add this
destination to ApplicationLoaders/WebContent/WEB-INF/flex/proxy-config.xml.)
Example 7-41. Proxy destination for Yahoo! News
<destination id="YahooNews"> <properties> <url>http://rss.news.yahoo.com/rss/topstories</url> </properties> </destination>
When you run the application and click OK on the pop up called “Loaded application talks back,” it will look like Figure 7-17.
What about the scenarios that support multiversioning? The default loading scenario from different web domains is Different Sandbox Different Domain. Example 7-42’s sample portal, DifferentSandboxDifferentDomainDemo, not only illustrates this scenario, it will also help you to understand the Same Sandbox Different Domain scenario.
When you examine the code, notice the seemingly redundant
reference to the class PopUpManager
.
It’s not accidental. You always have to link the PopUpManager
class to your portal to allow
pop-up controls in the portlets. That’s how Adobe implemented it, and
this requirement does not seem like too much to ask for.
Next, note that casting across sibling domains is out of reach.
Look at the body of the modifyValue()
method. You can’t cast the loadedApplication
either to IGreeting
or to
mx.core.Application
.
Instead, the example declares it as flash.display.
DisplayObject
. For similar reasons,
the declaration of the onLoadedApplicationCreated()
method downcasts
the type of object to flash.events.Event
. If you instead try to
declare loadedApplication
as Application
, you will receive this runtime
error:
TypeError: Error #1034: Type Coercion failed: cannot convert TrustfulApplication@c8f20a1 to mx.core.Application.
Now, examine the function onLoadComplete()
. To obtain the reference to
the sharedEventDispatcher
, the function
uses the expression swfLoader.swfBridge
instead of swfLoader.content.loaderInfo.sharedEvents
.
Example 7-42. DifferentSandboxDifferentDomainDemo
<?xml version="1.0"?> <!-- DifferentSandboxDifferentDomainDemo.mxml --> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:local="*" > <mx:Script> <![CDATA[ import events.RemoteEvent; import mx.managers.PopUpManager; PopUpManager; import mx.managers.SystemManager; private const APP_URL:String = "http://127.0.0.1:8080/RemoteSite/TrustfulApplication.swf"; public function modifyValue():void { var loadedApplication:DisplayObject = swfLoader.content["application"]; loadedApplication["setGreeting"]("loaded from outside"); } private function loadApplication():void { swfLoader.addEventListener("complete", onLoadComplete); swfLoader.source=APP_URL; } [Bindable] private var applicationLoaded:Boolean; private var sharedEventDispatcher:IEventDispatcher; private function onLoadComplete(event:Event):void { swfLoader.removeEventListener("complete", onLoadComplete); applicationLoaded = true; // Since swfLoader.content.loaderInfo.sharedEvents=null, // use swfLoader.swfBridge sharedEventDispatcher = swfLoader.swfBridge; sharedEventDispatcher.addEventListener("creationComplete", onLoadedApplicationCreated); } [Bindable] private var reply:String=""; // We cannot cast RemoteEvent across Application Domains private function onLoadedApplicationCreated(event:/*RemoteEvent*/ Event):void { if (event.hasOwnProperty("data")) { reply = event["data"]; } var remoteEvent:RemoteEvent = new RemoteEvent("command"); remoteEvent.data = ". Two-way communication works!"; sharedEventDispatcher.dispatchEvent(remoteEvent); } ]]> </mx:Script> <mx:HBox> <mx:Button label="Load Application" click="loadApplication()" /> <mx:Button label="Modify Value" click="modifyValue();" enabled="{applicationLoaded}"/> </mx:HBox> <mx:Panel title="Yahoo News{reply}" width="100%" height="50%" id="panel"> <local:YahooNews width="100%" height="100%"/> </mx:Panel> <mx:SWFLoader id="swfLoader" width="100%" height="50%"/> </mx:Application>
The same concepts hold true for the Same Sandbox Different Domain
scenario as well. Specific to the cross-domain scenario, however, is
that DifferentSandBoxDifferentDomainDemo loads
TrustfulApplication.swf (Example 7-43), which extends the RegularApplication
merely to express
cross-scripting trust to the web domain of the portal via Security.allowDomain("*")
.
Example 7-43. TrustfulApplication
<?xml version="1.0"?> <!-- TrustfulApplication.mxml--> <RegularApplication xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" preinitialize="onPreinitialize(event)"> <mx:Script> <![CDATA[ // Try to use without allowDomain and see the r.t. SecurityError private function onPreinitialize(event:Event):void { Security.allowDomain("*"); //localhost, wwww.adobe.com, etc. } ]]> </mx:Script> </RegularApplication>
The body of the function modifyValue()
takes advantage of these
cross-scripting permissions,
referring to swfLoader.content
. Had
you loaded the untrusted RemoteApplication.swf, you would
have received the error shown in Example 7-44.
Example 7-44. Example of security error
SecurityError: Error #2121: Security sandbox violation: Loader.content: http://localhost:8080/ApplicationLoaders/DifferentSandboxCommunicationDemo.swf cannot access http://127.0.0.1:8080/RemoteSite/RegularApplication.swf. This may be worked around by calling Security.allowDomain. at flash.display::Loader/get content() at mx.controls::SWFLoader/get content
This is the only coding specific to the DSDD scenario versus SSDD.
Of course, in the case of SSDD, the loadingForCompatibility
property of the
swfLoader
would be set to true, and
you would specify trustContent="true"
to offset the domain difference.
The successfully running DSDD application was previously presented in Figure 7-16, and Figure 7-18 illustrates a problem in the SSDD scenario: the Google News panel is showing up empty. As it turns out, in the case of SSDD, you need to change your architecture and preload Flex messaging, RPC, and Data Management Services–related classes in the application domain that will parent the domain of the portal.
The previous section mentioned that casting is out of reach across
sibling domains. That constraint is not as tight, however, as you might
think. Remember how you cast loaded modules and applications to the
IGreeting
interface earlier in the
chapter? You did not cast the IGreeting
of the child to
the IGreeting
of the
parent, because the IGreeting
of the child did not exist. A child
is always reusing classes loaded in the parental chain. So, two sibling
domains can cast classes if they share a common parent that preloads
these classes. In particular, such bootstrap class
loading, as Adobe calls it, is required to maintain a common
definition of the following classes from the mx.messaging.
messages
package per security
domain:
ConfigMap
AcknowledgeMessage
AcknowledgeMessageExt
AsyncMessage
AsyncMessageExt
CommandMessage
CommandMessageExt
ErrorMessage
HTTPRequestMessage
MessagePerformanceInfo
RemotingMessage
SOAPMessage
In the Different Sandbox Different Domain scenario, the portal and
portlet reside in the different sandboxes, so bootstrap loading of the
Flex messaging classes is not an issue. However, in the Same Sandbox
Different Domain scenario, the absence of the common bootstrap loader
results in the first application that happens to load these classes into
its own domain (be that portal or portlet) to block all other siblings
from receiving messages from the
MessageBroker
.
At Farata Systems, we customized PortalBootstrapLoader, which is a separate ActionScript project (Figure 7-19).
As you study the code for PortalBootstrapLoader in Example 7-45, notice that in addition to linking
in all classes required by Adobe, we also link in the class com.
farata.portal.Message
. Follow this
pattern to link in any class that you want to make available for all
portlets in your portal (and the portal itself).
Example 7-45. PortalBootstrapLoader.as
//PortalBootstrapLoader.as
package {
import flash.display.Loader;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.net.URLRequest;
import flash.system.ApplicationDomain;
import flash.system.LoaderContext;
import flash.system.SecurityDomain;
import utils.QueryString;
import mx.messaging.config.ConfigMap; ConfigMap;
import mx.messaging.messages.AcknowledgeMessage; AcknowledgeMessage;
import mx.messaging.messages.AcknowledgeMessageExt; AcknowledgeMessageExt;
import mx.messaging.messages.AsyncMessage; AsyncMessage;
import mx.messaging.messages.AsyncMessageExt; AsyncMessageExt;
import mx.messaging.messages.CommandMessage; CommandMessage;
import mx.messaging.messages.CommandMessageExt; CommandMessageExt;
import mx.messaging.messages.ErrorMessage; ErrorMessage;
import mx.messaging.messages.HTTPRequestMessage; HTTPRequestMessage;
import mx.messaging.messages.MessagePerformanceInfo; MessagePerformanceInfo;
import mx.messaging.messages.RemotingMessage; RemotingMessage;
import mx.messaging.messages.SOAPMessage; SOAPMessage;
import com.farata.portal.Message;Message;
public class PortalBootstrapLoader extends Sprite {
public function PortalBootstrapLoader() {
super();
if (ApplicationDomain.currentDomain.hasDefinition("mx.core::UIComponent"))
throw new Error("UIComponent should not be in the bootstrap loader.");
if (ApplicationDomain.currentDomain.hasDefinition("mx.core::Singleton"))
throw new Error("Singleton should not be in the bootstrap loader.");
if (stage) {
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
} else
isStageRoot = false;
root.loaderInfo.addEventListener(Event.INIT, onInit);
}
/**
* The Loader that loads the main application's SWF file.
*/
private var loader:Loader;
/**
* Whether the bootstrap loader is at the stage root or not,
* it is the stage root only if it was the root
* of the first SWF file that was loaded by Flash Player.
* Otherwise, it could be a top-level application but not stage root
* if it was loaded by some other non-Flex shell or is sandboxed.
*/
private var isStageRoot:Boolean = true;
/**
* Called when the bootstrap loader's SWF file has been loaded.
* Starts loading the application SWF specified by the applicationURL
* property.
*/
private function onInit(event:Event):void {
loader = new Loader();
var loaderContext:LoaderContext = new LoaderContext(
false,
new ApplicationDomain(ApplicationDomain.currentDomain),
SecurityDomain.currentDomain
);
addChild(loader);
loader.load(new URLRequest(applicationUrl), loaderContext );
loader.addEventListener(
"mx.managers.SystemManager.isBootstrapRoot",
bootstrapRootHandler
);
loader.addEventListener(
"mx.managers.SystemManager.isStageRoot",
stageRootHandler
);
loader.addEventListener(Event.ADDED, resizeHandler );
stage.addEventListener(Event.RESIZE, resizeHandler);
}
private function get applicationUrl():String{
var qs:QueryString = new QueryString();
return qs.root + qs.parameters.app;
}
private function bootstrapRootHandler(event:Event):void {
event.preventDefault();
}
private function stageRootHandler(event:Event):void {
if (!isStageRoot)
event.preventDefault();
}
private function resizeHandler(event:Event=null):void {
if ( loader.content ){
Object(loader.content).setActualSize(stage.stageWidth, stage.stageHeight);
}
}
}
}
To use the bootstrap loader, we copy PortalBootstrapLoader.html and PortalBootstrapLoader.swf to the deployment folder of the portal and, in the browser, type the URL, similar to:
http://localhost:8080/ApplicationLoaders/PortalBootstrapLoader.html?app=ApplicationLoaders/SameSandboxDifferentDomain.swf |
As you can see from Figure 7-20, now the Google News panel of the portlet is filled by the data. Flex Messaging works because we made the definitions of the messaging classes visible to all application domains in the portal.
18.218.137.93