This chapter aims to summarize some of the more common error scenarios. It was compiled based on questions asked on the forum and will most likely grow over time. It would be nice if you'd follow the guidelines in this chapter and do some investigation yourself before you post on the forum in case you run into any kind of problem. Please start with 16.1 Configure Logging, as logging should be turned on in all debugging scenarios, and then jump to the section which applies to your problem.
Some services in Parsley and also Flex Bindings in general do not rethrow errors. Thus you'd be completely tapping
in the dark for some sorts of errors if logging is not turned on. For debugging Parsley applications it's usually sufficient
to set a filter for org.spicefactory.parsley.*
and avoid the logs from the underlying Spicelib, as the reflection cache
emits some pretty verbose logs which would distract from the things you'd be interested in.
Log output from Parsley covers things like Contexts getting created or destroyed, objects getting configured or removed from the Context, messages getting dispatched or views getting wired.
The logging setup is usually done in the top level application class. It differs depending on the environment:
Flex 4
<fx:Declarations>
<s:TraceTarget
includeCategory="true"
includeLevel="true"
includeTime="true"
level="{LogEventLevel.DEBUG}"
>
<s:filters>
<fx:String>org.spicefactory.parsley.*</fx:String>
</s:filters>
</s:TraceTarget>
</fx:Declarations>
Flex 3
<mx:TraceTarget
includeCategory="true"
includeLevel="true"
includeTime="true"
level="{LogEventLevel.DEBUG}"
>
<mx:filters>
<mx:String>org.spicefactory.parsley.*</mx:String>
</mx:filters>
</mx:TraceTarget>
Flash
For Flash it is recommended to use XML configuration files as described in 15.2 Logging Configuration for Flash. If you want to use other configuration mechanisms for the rest of the application, you can still combine the XML for the logging setup with the other configuration artifacts:
FlashLoggingXmlSupport.initialize();
ContextBuilder
.newBuilder()
.config(XmlConfig.forFile("logging.xml"))
.config(ActionScriptConfig.forClass(MyApplicationConfig))
.build();
Even after you have configured logging as described in the preceding section, you still might run into a situation where an expected action is not performed while the logging output remains completely silent about it. Since Parsley generally logs all errors this usually hints at a problem where the framework does not even attempt to perform the desired action. This is usually caused by some sort of configuration error. The following sections list the most common scenarios that lead to silent failure:
The object is not managed
Some developers new to the concept of IOC containers simply add metadata like [Inject]
to a class and then create
an instance with new
and are suprised that nothing happens. Such an instance is not known to the framework and thus
will never get configured. Another common error is to add the class to a Parsley configuration file or class as required and then
create an instance with new
. This is a similar problem: Now you have two instances of the same class, but the one you
created with new
is not managed by the framework.
If you are not familiar with the concept of managed objects, please read 8.1 About Managed Objects first.
If you are not sure whether the object you are working with is managed, you can use the framework API to find out:
var obj:SomeClass = ...; // the object that does not work
trace("managed? " + ContextUtil.isManaged(obj));
Apart from that you'll also find logs for all objects that are getting added or removed from a Context. They look like this:
21:09:07.967 [INFO] org.spicefactory.parsley.core.lifecycle.impl.DefaultManagedObjectHandler
Configure managed object with [ObjectDefinition(type = com.foo::MyClass, id = someId)]
and 2 processor(s)
Metadata not compiled into the SWF or SWC
Another very common error. The mxmlc compiler of the Flex SDK has a confusingly inconsistent behavior when it comes to compiling metadata into SWFs. The outcome is different whether you merge Parsley into the code or whether you specify it as an external library. As a consequence it usually works in the top level application without additional configuration steps, but then fails when used in a Flex Module for example.
To find out whether metadata has actually been compiled into your application, you can trace the reflection output of the Flash Player for that class:
var obj:SomeClass = ...; // the object that does not work
trace(describeType(obj));
Here you can see whether the metadata is present on the property or method where you expect it. For Flex Modules or RSLs the metadata usually has to be specified explicitly. For detailed instructions see 3.1 Configuration with AS3 Metadata, specifically the section titled Compiling custom metadata into SWFs.
Metadata on private or protected members
The Reflection facility in the Flash Player will only consider metadata on public members. This is not a limitation of the Parsley framework, it's the way the Flash Player works. Thus the following will simply be ignored, you won't get any error messages:
[Inject]
private var service:RemoteObject;
Typos in metadata
Although this is pretty obvious it's worth mentioning it as this also leads to silent failure. When you misspell an attribute on a Parsley metadata tag the framework will throw an Error, but if you misspell the tag name itself it will simply get ignored.
Timing issues with object initialization
Also a pretty common scenario. The dependency injection does get performed in this case, just not at the time the developer expects it. The most extreme example is trying to access the value of a variable marked with [Inject] in the constructor of an object. It should be pretty obvious that it is virtually impossible that the injection has already happened at this point.
Somewhat less obvious but still very common is to use a Flex component event like addedToStage
or
creationComplete
and then access an expected injected value in the event handler. Please don't do this!
There are lots of reasons not to use addedToStage
for component initalization, some of them are not even related to
Parsley. For the full story, please read 9.7 Component Lifecycle and specifically the section titled
Be careful with Component Initialization Logic.
As a very short excerpt of that section, a safe place to put some initialization logic
is a method marked with [Init]
. This will only be invoked after all injections have been performed:
[Inject]
public var service1:SomeService;
[Inject]
public var service2:SomeOtherService;
[Init]
public function init () : void {
// here it is guaranteed that service1 and service2 have been set already
}
For wiring components it's important to understand the lifecycle of Flex components in relation to Parsley object management. One problem with misplaced initialization logic (that does not only affect views) has already been described in the preceding section under Timing issues with object initialization. This section lists some of the most frequent issues purely related to wiring views.
Errors when re-adding a view to the stage
The default behavior for a wired view (no matter whether you are using view autowiring or the <Configure>
tag)
is to remove the view from the Context once the view is removed from the stage. This is the most convenient option as this is
what is required in many scenarios where views are not developed for reuse. But when you do reuse a view and are not aware
of this behavior you will encounter unexpected behavior. Dependencies like presentation models might get injected for a second
time for example, causing the view to lose its internal state.
Fortunately there are ways to prevent this, as this is just the default behavior. For more details see 9.7 Component Lifecycle, preferrably including the section Beware of Memory Leaks as changing the default behavior might indeed solve your problem, but at the same time introduce leaks if not done correctly.
If the <ContextBuilder>
tag itself is placed into a view, you might even encounter the situation where
the entire Context gets destroyed when you remove the view from the stage. Again this is only the default that can
be switched off like explained in 9.7 Component Lifecycle.
Error: Object of type [SomeViewClass] is already managed
Parsley 2.4 introduced checks to prevent the same object getting wired twice, potentially to two different Contexts which could cause non-deterministic behavior. If you see this error it might have any of the following causes:
Configure
tag and API you need at least version 2.4.0, if you are using View Autowiring you need at least version 2.4.1. Earlier
versions had bugs in the way they performed the checks for duplicate wiring. Configure
tag with View Autowiring (setting the autowireComponents
property to true in the
ViewSettings
tag). configureIOC
or configureView
. Problems with popups and AIR windows
When wiring views in popups or AIR windows some additional steps need to be performed as these types of components are not placed into the normal view hierarchy below the root Application component. The basics are explained in 9.8 Flex Popups and Native AIR Windows. If you follow these instructions and still run into problems, they might be caused by one of the following issues:
Applications that load modules are usually also multi-Context applications, as each module normally creates at least
one internal Context within the module. Errors when working with modules usually occur when the Contexts in the various layers
do not "connect" correctly or when a view is wired to the wrong Context. This often comes as a surprise as there is a lot of
magic happening under the hood when you load a module and/or create a child Context. Parsley usually connects them automatically,
creating a Context hierarchy that matches the view hierarchy the Contexts were created in. This way any child Context does always
automatically share all dependencies from a parent Context. In some scenarios this magic does not work, most often due to a configuration
issues. In very rare cases it might even be necessary to specify the parent Context and/or ApplicationDomain manually, which can
be done through attributes of the <ContextBuilder>
tag for example.
Reflection Errors ("Specified ApplicationDomain does not contain the class MyClass")
This can only happen when you load modules into child ApplicationDomains. Reflection errors usually have one of the following two causes:
<ContextBuilder/>
tag
to the root module component. Missing Dependencies
If you see errors that a specific dependency cannot be found, although you are sure that you declared it in a Context configuration file, then it is very likely that the view got wired to the wrong Context. See Problems with popups and AIR windows for a common error scenario.
Examining the log output also helps in this case, as it allows you to easily find out which Context connected to which
parent and which view got wired to which Context. For an example lets assume that you create the root application Context
using MyRootConfig
as the MXML configuration class and then MyModuleConfig
as the configuration for the
module. Inside the module you have a view of type MyWindow
that needs to get wired. If all goes well you should
find these three entries in the log output (intertwined with other logs):
22:18:54.953 [INFO] org.spicefactory.parsley.core.bootstrap.impl.DefaultBootstrapConfig
Creating Context [Context(FlexConfig{MyRootConfig})] without parent
[...]
22:19:01.270 [INFO] org.spicefactory.parsley.core.bootstrap.impl.DefaultBootstrapConfig
Creating Context [Context(FlexConfig{MyModuleConfig})]
with parent [Context(FlexConfig{MyRootConfig})]
[...]
22:19:01.544 [DEBUG] org.spicefactory.parsley.core.view.impl.DefaultViewConfigurator
Add view 'My_Module.ApplicationSkin2._AppSkin_Group1.contentGroup.TestModule23.MyWindow25'
to [Context(FlexConfig{MyModuleConfig})]
Here you can examine that the module Context found its parent (the root ApplicationContext) and the view MyWindow got wired to the module Context and not to the root Context.
You can also improve the readability of the logs when you set the description
attribute
explicitly in the ContextBuilder:
<parsley:ContextBuilder config="{MyRootConfig}" description="root"/>
This would turn the output [Context(FlexConfig{MyRootConfig})]
into just
[Context(root)]
. This does not make such a big difference if you use only one configuration class,
but if you use multiple ones the default description would list them all.