Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Retry validation if it fails #103

Closed
LibertyWalk opened this issue Mar 7, 2019 · 5 comments
Closed

Retry validation if it fails #103

LibertyWalk opened this issue Mar 7, 2019 · 5 comments

Comments

@LibertyWalk
Copy link

I have a consumer, which commands its provider to connect to a server. This server might not be online at the point in the the iPOPO bundle is started. This, of course, invalidates the consumer and stops it from ever being executed without restarting the whole bundle.
First off, I'm not sure about the purpose of @Validate. Is this supposed to validate the consumer, if all its prerequisites (like the server connection) are valid? Or is it just, like the docs say, for validating that all of its required dependencies (like for example its provider) have been injected?
Second, is there a mechanism for trying to validate a component until it succeeds, instead of just declaring it as invalidated? I tried to do that in the @Validate function of my consumer. The consumer commanded the provider to try to connect to the server until it succeeds in doing so, but when the connection finally succeeded, a TimeOutError was thrown inside of the @Validate of the consumer.

Is something like waiting for a connection even compatible with iPOPO?

Thanks for developing this project.

@fijemax
Copy link

fijemax commented Mar 7, 2019

I did someting like it for a websocket handler.
The websocket handler bundle must provide his service only when the websocket connexion is available.

To provide a service manually:

Do not use the @Provides annotation

First save the contexte handler you received in the validate method

    @Validate
    def validate(self, context):
        self._context = context

Create two methods to register and unregister you service:

    def _register_service(self):
        self.logger.info('WebsocketHandler service validated')
        if not self._service_registration:
            self._service_registration = self._context.register_service(
                "websocket_handler_service", self, {})

    def _unregister_service(self):
        if self._service_registration:
            self.logger.info('WebsocketHandler service invalidated')
            self._context.get_framework().unregister_service(
                self._service_registration)
            self._service_registration = None

You simply need to call _register_service or _unregister_service when you want provide your service or not.
In my my case when websocket are available the service is registered and all dependent services are notifyed or validated.

websocket_handler_service is the name to require in others bundles

@fijemax
Copy link

fijemax commented Mar 7, 2019

@fijemax
Copy link

fijemax commented Mar 7, 2019

A websocket handler sample

sample.zip

@tcalmant
Copy link
Owner

tcalmant commented Mar 7, 2019

Hi,

Like @fijemax said, your provider must be an instantiated component that registers the service your consumer requires.
Unlink most component models, where required components are spawned automatically by the framework; in iPOPO you have to instantiate both the consumer and the provider with their configuration. The provider will have to indicate if it provides the service or not (like in @fijemax example).

That being said, I'll just add some notes:

  • If you want to allow a grace period between two connections, you can use the @Temporal in place of @Requires: all calls to the dependency will block until a dependency is re-injected or the given timeout expired.

  • @fijemax : you can simplify your code using @Provides' controller argument. For example:

@ComponentFactory()
@Provides('provider_service', '_svc_flag')
class SampleProvider(object):
    @Validate
    def validate(self, context):
         # Disable the service at first
         self._svc_flag = False
         # Start your connection process, in this example I'll consider you have to register
         # some callback, like on_connect, on_disconnect

    def on_connect(self):
        """ Called back when your connection is up """
        # Register the service:
        self._svc_flag = True

    def on_disconnect(self)
        """ Called back when your connection is down """
        # Unregister the service:
        self._svc_flag = False

Note that a lot of stuff is done when changing the value of the flag (calls register_service(), notifies listeners, possibly in/validates components, ...).

@tcalmant
Copy link
Owner

tcalmant commented Mar 7, 2019

Also, to reply to the original question:

  • if all the requirements of a component are not present, the component won't be validated. The validation will occur once all requirements have been injected. The component then passes from INVALID to VALID state.
  • if the validation method raises an exception, then the component passes to ERRONEOUS state and will stay there until you call the ipopo.retry_erroneous() method.

This has to be done manually, as the exception means there is something wrong either with the code, the configuration or an injected service.
Note that you you should be able to call retry_erroneous() from the component itself, in an BindField callback (but I never tried it as it seems more like a last-resort trick)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants