You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@karaf.apache.org by Xtra Coder <xt...@gmail.com> on 2019/03/07 03:48:19 UTC

@Reference(service = MyApi.class) is not re-populated

I'm trying to use Declarative Services as outlined here
https://blog.osgi.org/2018/03/osgi-r7-highlights-declarative-services.html 
... and I have the problem when referenced service does not re-appear after
restart of its bundle.

In my scenario i need to dispatch invocation of a method call to number of
implementations.
To do this I create 3 implementation bundles, each of them has component
like this

	public interface MyApi {
		String sayHello(String why);
	}

	@Component
	public class MyApiImpl1 implements MyApi {

		public MyApiImpl1() {
			sayHello("startup");
		}

		public String sayHello(String why) {
			System.out.println("Hello from Impl1: " + why);
			return "impl1";
		}
	}

And i also have dispatcher bundle #4, having following code

	@Component
	public class MyApiDispatcher {
		@Reference(service = MyApi.class)
		List<MyApi> implementations;

		boolean stopped;
		Thread ping = new Thread(() -> {
			while( !stopped ) {
				test("ping");
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});

		public MyApiDispatcher() {
			ping.start();
		}		
		
		@Deactivate
		public void deactivate() {
			stopped = true;
		}

		public void test(String source) {
			if( implementations == null ) {
				System.out.println("implementations = <null>");
				return;
			}

			List<String> calledImpls = new ArrayList<>();

			for (MyApi impl : implementations) {
				calledImpls.add(impl.sayHello(source));
			}

			System.out.println("calledImpls(" + source + ") = " + calledImpls);
		}
	}

Behaviour as I see it now is following:

1. OK: initial startup - all 3 implementations are present in
MyApiDispatcher
2. OK: after i stop bundle with Impl#1 - it disappears from
MyApiDispatcher.implementations
3. BAD: after restart bundle with Impl#1 - it DOES NOT re-appear in
MyApiDispatcher.implementations
4. WORKAROUND: after restart of bundle with dispatcher - it receives all 3
implementations again

Behaviour #3 is very unexpected. 

Is it bug or OSGI spec defines such behaviour? 
How can I make impl re-appear after restart of its bundle?

My current environment is following:

  Karaf version               4.2.3
  OSGi Framework              org.apache.felix.framework-5.6.12









--
Sent from: http://karaf.922171.n3.nabble.com/Karaf-User-f930749.html

Re: @Reference(service = MyApi.class) is not re-populated

Posted by Xtra Coder <xt...@gmail.com>.
Your hints plus now looking into correct place of the OSGI spec bought me to
the solution which i was looking for (and it is different from the typical
CDI approach).

So ... for my scenario annotation should be actually attached to the setter
method (as I've found here
https://osgi.org/specification/osgi.cmpn/7.0.0/service.component.html#service.component-reference.element
-> sample about setLog).

Thanks for help. Here is sample for copy/paste.

    private final List<MyApi> implementations = new
CopyOnWriteArrayList<>();

    @Reference(service        = MyApi.class
               , cardinality  = ReferenceCardinality.MULTIPLE
               , policy       = ReferencePolicy.DYNAMIC
               , unbind       = "removeImpl"
               )
    private void setImpl(MyApi impl)    {
        System.out.println("---------------------------------------- +++++ =
" + instanceId + " + " + impl );
        implementations.add(impl); 
    }
    
    private void removeImpl(MyApi impl) {
        System.out.println("---------------------------------------- ----- =
" + instanceId + " - " + impl );
        implementations.remove(impl);
    }





--
Sent from: http://karaf.922171.n3.nabble.com/Karaf-User-f930749.html

Re: @Reference(service = MyApi.class) is not re-populated

Posted by David Jencks <da...@gmail.com>.
You can by having using the event strategy for your reference, i.e. having (suitably annotated) bind/unbind methods. I don’t think there’s any way to predict whether the field is updated before or after the bind/unbind method is called, so I would not have an annotated field but use the event method to update the field yourself.
The modified method is called when the configuration for a component is changed, and an updated method is called when the configuration for the reference is changed; I do t think you’ve mentioned any of your comments using configuration.

David Jencks 

Sent from my iPhone

> On Mar 7, 2019, at 4:31 PM, Xtra Coder <xt...@gmail.com> wrote:
> 
> Thanks very much, I was able to make first step with your hints.
> 
> Second step is - How to get notification about change in the set of
> provisioned references?
> 
> I've crafted test project from Karaf examples here ...
> https://github.com/xtracoder/karaf-experimental/blob/master/examples/karaf-scr-example/karaf-scr-example-client/src/main/java/org/apache/karaf/examples/scr/client/ConsoleClient.java#L38
> 
> When i stop/start one of the MyApi-implementation bundles, 'implementations'
> field do being updated as traced by background thread, but none of the
> `implementationsUpdated()` or `modified()` gets called.
> 
> Can I somehow get that notification from the OSGI framework about change of
> the 'implementations' field?
> 
> Sample code:
> - - - - - -
> 
>    @Reference(service        = MyApi.class
>               , cardinality  = ReferenceCardinality.MULTIPLE
>               , policy       = ReferencePolicy.DYNAMIC
>               , policyOption = GREEDY
>               , fieldOption  = FieldOption.UPDATE
>               , updated      = "implementationsModified"
>               )
>    private List<MyApi> implementations;
> 
> 
>    public void implementationsUpdated() throws Exception {
> 
> System.out.println("--------------------------------------------------------------------
> updat = " + instanceId + ": " + implementations);
>        running = false;
>    }
> 
>    @Modified
>    public void modified() throws Exception {
> 
> System.out.println("--------------------------------------------------------------------
> modif = " + instanceId + ": " + implementations);
>        running = false;
>    }
> 
> 
> 
> 
> 
> 
> --
> Sent from: http://karaf.922171.n3.nabble.com/Karaf-User-f930749.html

Re: @Reference(service = MyApi.class) is not re-populated

Posted by Xtra Coder <xt...@gmail.com>.
Thanks very much, I was able to make first step with your hints.

Second step is - How to get notification about change in the set of
provisioned references?

I've crafted test project from Karaf examples here ...
https://github.com/xtracoder/karaf-experimental/blob/master/examples/karaf-scr-example/karaf-scr-example-client/src/main/java/org/apache/karaf/examples/scr/client/ConsoleClient.java#L38

When i stop/start one of the MyApi-implementation bundles, 'implementations'
field do being updated as traced by background thread, but none of the
`implementationsUpdated()` or `modified()` gets called.

Can I somehow get that notification from the OSGI framework about change of
the 'implementations' field?

Sample code:
- - - - - -

    @Reference(service        = MyApi.class
               , cardinality  = ReferenceCardinality.MULTIPLE
               , policy       = ReferencePolicy.DYNAMIC
               , policyOption = GREEDY
               , fieldOption  = FieldOption.UPDATE
               , updated      = "implementationsModified"
               )
    private List<MyApi> implementations;


    public void implementationsUpdated() throws Exception {
       
System.out.println("--------------------------------------------------------------------
updat = " + instanceId + ": " + implementations);
        running = false;
    }

    @Modified
    public void modified() throws Exception {
       
System.out.println("--------------------------------------------------------------------
modif = " + instanceId + ": " + implementations);
        running = false;
    }






--
Sent from: http://karaf.922171.n3.nabble.com/Karaf-User-f930749.html

Re: @Reference(service = MyApi.class) is not re-populated

Posted by David Jencks <da...@gmail.com>.
This is expected and as per DS spec.
You can make your reference dynamic or greedy or both and it will work as you desire.

Your reference is static, so changing the bound services results in deactivating, recreating, and reactivating the component: a dynamic reference will just add and remove services with the original component instance.

Your reference is reluctant, so once created, a component instance will remain, with the same references, as long as possible; thus, when a reference disappears the component instance will be deactivated and reactivated with the reduced set of references, but when there is at least one reference, so the component instance has been activated, nothing will happen when a new reference appears.  A greedy reference will result in the component always having an up to date set of references. If the reference is static, this means the component instance will be deactivated, recreated, and activated with the new set of references.

David Jencks

> On Mar 6, 2019, at 7:48 PM, Xtra Coder <xt...@gmail.com> wrote:
> 
> I'm trying to use Declarative Services as outlined here
> https://blog.osgi.org/2018/03/osgi-r7-highlights-declarative-services.html 
> ... and I have the problem when referenced service does not re-appear after
> restart of its bundle.
> 
> In my scenario i need to dispatch invocation of a method call to number of
> implementations.
> To do this I create 3 implementation bundles, each of them has component
> like this
> 
> 	public interface MyApi {
> 		String sayHello(String why);
> 	}
> 
> 	@Component
> 	public class MyApiImpl1 implements MyApi {
> 
> 		public MyApiImpl1() {
> 			sayHello("startup");
> 		}
> 
> 		public String sayHello(String why) {
> 			System.out.println("Hello from Impl1: " + why);
> 			return "impl1";
> 		}
> 	}
> 
> And i also have dispatcher bundle #4, having following code
> 
> 	@Component
> 	public class MyApiDispatcher {
> 		@Reference(service = MyApi.class)
> 		List<MyApi> implementations;
> 
> 		boolean stopped;
> 		Thread ping = new Thread(() -> {
> 			while( !stopped ) {
> 				test("ping");
> 				try {
> 					Thread.sleep(2000);
> 				} catch (InterruptedException e) {
> 					e.printStackTrace();
> 				}
> 			}
> 		});
> 
> 		public MyApiDispatcher() {
> 			ping.start();
> 		}		
> 		
> 		@Deactivate
> 		public void deactivate() {
> 			stopped = true;
> 		}
> 
> 		public void test(String source) {
> 			if( implementations == null ) {
> 				System.out.println("implementations = <null>");
> 				return;
> 			}
> 
> 			List<String> calledImpls = new ArrayList<>();
> 
> 			for (MyApi impl : implementations) {
> 				calledImpls.add(impl.sayHello(source));
> 			}
> 
> 			System.out.println("calledImpls(" + source + ") = " + calledImpls);
> 		}
> 	}
> 
> Behaviour as I see it now is following:
> 
> 1. OK: initial startup - all 3 implementations are present in
> MyApiDispatcher
> 2. OK: after i stop bundle with Impl#1 - it disappears from
> MyApiDispatcher.implementations
> 3. BAD: after restart bundle with Impl#1 - it DOES NOT re-appear in
> MyApiDispatcher.implementations
> 4. WORKAROUND: after restart of bundle with dispatcher - it receives all 3
> implementations again
> 
> Behaviour #3 is very unexpected. 
> 
> Is it bug or OSGI spec defines such behaviour? 
> How can I make impl re-appear after restart of its bundle?
> 
> My current environment is following:
> 
>  Karaf version               4.2.3
>  OSGi Framework              org.apache.felix.framework-5.6.12
> 
> 
> 
> 
> 
> 
> 
> 
> 
> --
> Sent from: http://karaf.922171.n3.nabble.com/Karaf-User-f930749.html