robotlegs / robotlegs-framework

An ActionScript 3 application framework for Flash and Flex
https://robotlegs.tenderapp.com/
MIT License
967 stars 261 forks source link

Injector is missing a mapping to handle injection #151

Closed ghost closed 11 years ago

ghost commented 11 years ago

We want to update our application to RL 2.1.0 and we ran into some problems.

We changed the Injector properties to IInjector and came to this error message:

Injector is missing a mapping to handle injection into property "event" of object "[object ScanCommand]" with type "library.commands::ScanCommand". Target dependency: "flash.events::Event|"
    at org.swiftsuspenders.typedescriptions::PropertyInjectionPoint/applyInjection()[/Development/Projects/SwiftSuspenders/src/org/swiftsuspenders/typedescriptions/PropertyInjectionPoint.as:50]
    at org.swiftsuspenders::Injector/applyInjectionPoints()[/Development/Projects/SwiftSuspenders/src/org/swiftsuspenders/Injector.as:692]
    at org.swiftsuspenders::Injector/instantiateUnmapped()[/Development/Projects/SwiftSuspenders/src/org/swiftsuspenders/Injector.as:430]
    at org.swiftsuspenders::Injector/getOrCreateNewInstance()[/Development/Projects/SwiftSuspenders/src/org/swiftsuspenders/Injector.as:407]
    at robotlegs.bender.extensions.commandCenter.impl::CommandExecutor/executeCommand()[C:\trunk\robotlegs\src\robotlegs\bender\extensions\commandCenter\impl\CommandExecutor.as:84]
    at robotlegs.bender.extensions.commandCenter.impl::CommandExecutor/executeCommands()[C:\trunk\robotlegs\src\robotlegs\bender\extensions\commandCenter\impl\CommandExecutor.as:65]
    at robotlegs.bender.extensions.eventCommandMap.impl::EventCommandTrigger/eventHandler()[C:\trunk\robotlegs\src\robotlegs\bender\extensions\eventCommandMap\impl\EventCommandTrigger.as:118]

Part of our FrameworkConfig file:

    public class FrameworkConfig
    {
        [Inject]
        public var commandMap:IEventCommandMap;

        [Inject]
        public var injector:IInjector;

        /**
         * Configures the model, view and controller for Robotlegs
         */
        [PostConstruct]
        public function configure():void
        {
            // commands
            commandMap.map(NextEvent.NEXT, NextEvent).toCommand(ScanCommand);
            commandMap.map(PreviousEvent.PREVIOUS, PreviousEvent).toCommand(ScanCommand);
            commandMap.map(SelectEvent.SCANNER_SELECT, SelectEvent).toCommand(ScanCommand);
            commandMap.map(SelectEvent.POINTER_SELECT, SelectEvent).toCommand(ScanCommand);
            commandMap.map(ChangeScreenEvent.CURRENT_SCREEN_CHANGED, ChangeScreenEvent).toCommand(ScanCommand);
        }
    }

And the relevant ScanCommand:

    public class ScanCommand extends BaseCommand
    {

        [Inject]
        public var event:Event;

        override public function execute():void
        {
            // (...)
        }
    }

It seems that the specific events (e.g. SelectEvent, ChangeScreenEvent, ...) can not be injected into the event property of the ScanCommand typed the super class Event.

This worked with the old Robotlegs 2.0 preview (there wasn't any version number).

How can we get this back to work?

creynders commented 11 years ago

Yes, previously events were temporarily mapped with the injector to both the abstract Event type and their concrete type, we dropped that and switched to looking at how the event is mapped with the command map. In case it's mapped to a concrete type then the event will be injected as a concrete type as well: (as you already found out)

//wiring
commandMap.map(NextEvent.NEXT, NextEvent).toCommand(ScanCommand);

//command
[Inject]
public var event : NextEvent;

However if it's mapped to Event it will be injected as of type Event:

//wiring
commandMap.map(NextEvent.NEXT, Event).toCommand(ScanCommand);

//command
[Inject]
public var event : Event;

Would this suffice? In case not, could you explain in a little more detail why exactly you need it as an abstract event in the commands?

ghost commented 11 years ago

Thanks for the quick reply. Your proposed solution works fine!

However we ran into a new problem with the injector.injectInto() method.

Error: Injector is missing a mapping to handle injection into property "fileWriter" of object "[object MediaCommand]" with type "library.editor.commands::MediaCommand". Target dependency: "library.services.writers::JPEGFileWriter|"

Again our FrameworkConfig file with the injector call:

    public class FrameworkConfig
    {
        [Inject]
        public var commandMap:IEventCommandMap;

        [Inject]
        public var injector:IInjector;

        /**
         * Configures the model, view and controller for Robotlegs
         */
        [PostConstruct]
        public function configure():void
        {
            // services
            injector.injectInto(JPEGFileWriter);

            // commands 
            commandMap.map(MediaEvent.LOAD_IMAGE, MediaEvent).toCommand(MediaCommand);
            commandMap.map(MediaEvent.SAVE_IMAGE, MediaEvent).toCommand(MediaCommand);
            commandMap.map(MediaEvent.LOAD_VIDEO, MediaEvent).toCommand(MediaCommand);
            commandMap.map(MediaEvent.RECORD_VIDEO, MediaEvent).toCommand(MediaCommand);
        }
    }

An here our command with the JPEGFileWriter property.

    public class MediaCommand extends BaseCommand
    {

        [Inject]
        public var event:MediaEvent;

        [Inject]
        public var fileWriter:JPEGFileWriter;

        override public function execute():void
        {
            // (...)
        }
    }

Have you any idea whats causing this problem?

creynders commented 11 years ago

There's a few things that go wrong:

  1. You're trying to inject values into a Class. IInjector#injectInto only works with instances, not classes
  2. As far as I can tell JPEGFileWriter is never registered with the injector, therefore it can't be injected into the command

So, I'm assuming that with injector.injectInto(JPEGFileWrite) you actually want to register JPEGFileWriter to be injected into other class's instances? Then you should map it with the injector. There's a few possibilities:

  1. either you map it as a singleton (in which case all dependants will be injected with the same instance of JPEGFileWriter

    injector.map(JPEGFileWriter).asSingleton();

  2. or you map it that all dependants will be injected with a new, fresh instance of JPEGFileWriter

    injector.map(JPEGFileWriter).toType(JPEGFileWriter);

  3. or you create the instance yourself and map it as a value, then all dependants will be injected with that specific instance

    var writer = new JPEGFileWriter();
    injector.injectInto(write); //only necessary if JPEGFileWriter itself has dependencies that need to be injected
    injector.map(JPEGFileWriter).toValue(write);
ghost commented 11 years ago

Thanks for your very good explenation about injectInto()! We thought, injectInto() will inject the given object (or as we thought class) into every other class where it's required by [Inject]. But it's exact the other way round.

Then we figured out that we did not even need the injectInto() call, because we want to create a new JPEGFileWriter instance everytime it is injected into our MediaCommand. Now we skip the injection and just create a new JPEGFileWriter instance right inside the MediaCommand.

Now everything seems to work. Thanks again for your help.

tahiralvi commented 10 years ago

Hi,

I have faced the same issue, following error though by the compiler. Error: Injector is missing a mapping to handle injection into property "presentServiceSO" of object "[object PresentService]" with type "org.bigbluebutton.core.services.present::PresentService". Target dependency: "org.bigbluebutton.core.services.present::IPresentServiceSO|" at org.swiftsuspenders.typedescriptions::PropertyInjectionPoint/applyInjection()[/Development/projects/SwiftSuspenders/src/org/swiftsuspenders/typedescriptions/PropertyInjectionPoint.as:50] at org.swiftsuspenders::Injector/applyInjectionPoints()[/Development/projects/SwiftSuspenders/src/org/swiftsuspenders/Injector.as:692] at org.swiftsuspenders::Injector/instantiateUnmapped()[/Development/projects/SwiftSuspenders/src/org/swiftsuspenders/Injector.as:430] at org.swiftsuspenders.dependencyproviders::SingletonProvider/createResponse()[/Development/projects/SwiftSuspenders/src/org/swiftsuspenders/dependencyproviders/SingletonProvider.as:61] at org.swiftsuspenders.dependencyproviders::SingletonProvider/apply()[/Development/projects/SwiftSuspenders/src/org/swiftsuspenders/dependencyproviders/SingletonProvider.as:49] at org.swiftsuspenders.typedescriptions::PropertyInjectionPoint/applyInjection()[/Development/projects/SwiftSuspenders/src/org/swiftsuspenders/typedescriptions/PropertyInjectionPoint.as:52] at org.swiftsuspenders::Injector/applyInjectionPoints()[/Development/projects/SwiftSuspenders/src/org/swiftsuspenders/Injector.as:692] at org.swiftsuspenders::Injector/instantiateUnmapped()[/Development/projects/SwiftSuspenders/src/org/swiftsuspenders/Injector.as:430] at org.swiftsuspenders::Injector/getOrCreateNewInstance()[/Development/projects/SwiftSuspenders/src/org/swiftsuspenders/Injector.as:407] at robotlegs.bender.extensions.commandCenter.impl::CommandExecutor/executeCommand()[C:\development\libs\robotlegs-framework\src\robotlegs\bender\extensions\commandCenter\impl\CommandExecutor.as:84] at robotlegs.bender.extensions.commandCenter.impl::CommandExecutor/executeCommands()[C:\development\libs\robotlegs-framework\src\robotlegs\bender\extensions\commandCenter\impl\CommandExecutor.as:65] at robotlegs.bender.extensions.signalCommandMap.impl::SignalCommandTrigger/routePayloadToCommands()[C:\development\libs\robotlegs-extensions-SignalCommandMap\src\robotlegs\bender\extensions\signalCommandMap\impl\SignalCommandTrigger.as:105] at org.osflash.signals::Slot/execute()[C:\development\libs\as3-signals\src\org\osflash\signals\Slot.as:92] at org.osflash.signals::OnceSignal/dispatch()[C:\development\libs\as3-signals\src\org\osflash\signals\OnceSignal.as:132] at org.bigbluebutton.command::JoinMeetingCommand/successJoined()[D:\BBB\Mobile\src\org\bigbluebutton\command\JoinMeetingCommand.as:52] at org.osflash.signals::Slot/execute()[C:\development\libs\as3-signals\src\org\osflash\signals\Slot.as:92] at org.osflash.signals::OnceSignal/dispatch()[C:\development\libs\as3-signals\src\org\osflash\signals\OnceSignal.as:132] at org.bigbluebutton.core.services::LoginService/afterEnter()[D:\BBB\Mobile\src\org\bigbluebutton\core\services\LoginService.as:102] at org.osflash.signals::Slot/execute()[C:\development\libs\as3-signals\src\org\osflash\signals\Slot.as:92] at org.osflash.signals::OnceSignal/dispatch()[C:\development\libs\as3-signals\src\org\osflash\signals\OnceSignal.as:132] at org.bigbluebutton.core.services::EnterService/onSuccess()[D:\BBB\Mobile\src\org\bigbluebutton\core\services\EnterService.as:37] at org.osflash.signals::Slot/execute()[C:\development\libs\as3-signals\src\org\osflash\signals\Slot.as:96] at org.osflash.signals::OnceSignal/dispatch()[C:\development\libs\as3-signals\src\org\osflash\signals\OnceSignal.as:132] at org.bigbluebutton.core.services::URLFetcher/handleComplete()[D:\BBB\Mobile\src\org\bigbluebutton\core\services\URLFetcher.as:52] at flash.events::EventDispatcher/dispatchEventFunction() at flash.events::EventDispatcher/dispatchEvent() at flash.net::URLLoader/onComplete()