API bundles make consumer bundles always "Active"?

55 views
Skip to first unread message

Rubén Pérez Vázquez

unread,
Nov 2, 2020, 5:26:47 PM11/2/20
to bndtool...@googlegroups.com
Dear all,

This is my third email about my little side-project to learn while doing something (potentially) useful with OSGi. So far, I have not gone as far as the "useful" stage, but I have been learning some things in the process. I got responses to my first question that helped me so much, and no responses to my second one... Let's see if I am lucky this time :)

I am trying to follow the conventions and best practices as much as possible. Therefore, I have created API bundles for services that are then implemented in other bundles. For instance, I have a very simple REST service:

package org.example.company.service.api;
 
import java.util.List;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.osgi.annotation.versioning.ProviderType;

import org.example.company.Company;

@Path("/companies")
@ProviderType
public interface CompanyService {

@GET
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
     public List<Company> getCompanies();
}


 , and the corresponding implementation:

package org.example.company.service.simple.impl;
 
import java.util.List;

import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ServiceScope;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.example.company.Company;
import org.example.company.provider.api.CompanyProvider;
import org.example.company.service.api.CompanyService;

@Component(scope = ServiceScope.SINGLETON)
@JaxrsResource
public class CompanyServiceSimpleImpl implements CompanyService {

  private static final Logger logger = LoggerFactory.getLogger(CompanyServiceSimpleImpl.class);

  @Reference
  CompanyProvider companyProvider;

  @Override
  public List<Company> getCompanies() {
   return companyProvider.getAll();
  }
}

This class contains a reference to a CompanyProvider service which, again, was defined in separate API and implementation bundles:

package org.example.company.provider.api;

import java.util.List;

import org.osgi.annotation.versioning.ProviderType;

import org.example.company.Company;

@ProviderType
public interface CompanyProvider {
  public void add(Company company) throws CompanyAlreadyExistsException;
  public Company get(String name) throws NoSuchCompanyException;
  public void update(Company company) throws NoSuchCompanyException;
  public void delete(Company company) throws NoSuchCompanyException;
  public List<Company> getAll();
}


package org.example.company.provider.inmemory.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.ServiceScope;

import org.example.company.Company;
import org.example.company.provider.api.CompanyAlreadyExistsException;
import org.example.company.provider.api.CompanyProvider;
import org.example.company.provider.api.NoSuchCompanyException;

@Component(scope = ServiceScope.SINGLETON)
public class InMemoryCompanyProvider implements CompanyProvider {

  private final ConcurrentMap<String, Company> companyMap = new ConcurrentHashMap<>();

  @Override
  public void add(final Company company) throws CompanyAlreadyExistsException {
   if (companyMap.putIfAbsent(company.getName(), company) == null) {
    throw new CompanyAlreadyExistsException(company);
   }
  }

  @Override
  public Company get(final String name) throws NoSuchCompanyException {
   final Company retVal = companyMap.get(name);
   if (retVal == null) {
    throw new NoSuchCompanyException(name);
   }
   return retVal;
  }

  @Override
  public void update(final Company company) throws NoSuchCompanyException {
   if (companyMap.replace(company.getName(), company) == null) {
    throw new NoSuchCompanyException(company);
   }
  }

  @Override
  public void delete(final Company company) throws NoSuchCompanyException {
   if (companyMap.remove(company.getName(), company)) {
    throw new NoSuchCompanyException(company);
   }
  }

  @Override
  public List<Company> getAll() {
   return new ArrayList<>(companyMap.values());
  }
}

If I include the provider implementation bundle in my bndrun file, then everything resolves and the service works (even if it does not do anything so usefule yet!). However, if I don't include the implementation bundle, my service bundle still has an Active status, even though a mandatory dependency is missing. Well, not really: the service depends on the API bundle, which is present, but because there is no bundle implementing it, the requirement is not initialized and therefore the service (understandably) does not work.

My question is: is it the intended behaviour that the bundle remains "Activated" even if not all its requirements are satisfied? Is there any best practice/more "idiomatic" way of accomplishing that?

Best regards,
Rubén

Raymond Auge

unread,
Nov 2, 2020, 5:57:12 PM11/2/20
to bndtool...@googlegroups.com
Bundles and "Declarative Service components" live along different axes!

The bundle can be active while no components are enabled if all requirements of a bundle are satisfied. That does not imply all services and/or configurations needed by components are available. This is a perfectly legitimate mode, although it is surprising to newcomers.

So, there are two avenues to diagnose:
- gogo shell along with Felix SCR gives access to commands to diagnose components (i.e. `src:list [bundleId]`, `scr:info <component_name>`)
- a (build time) bnd resolver plugin along with the instruction `-resolve.effective: active`

The idea for scr commands is to give you insight into what state each component is and what dependencies (if any) are missing.
The idea for the build time resolver plugins is to prevent you from assembling a runtime which does not have a service implementation for a service reference you need.

HTH,
- Ray

--
You received this message because you are subscribed to the Google Groups "bndtools-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to bndtools-user...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/bndtools-users/CAFWAsK3SpZKQEdF1cL52EWhwVVjN7oRSpQqDqY3XGsFRM%2BmUFA%40mail.gmail.com.


--
Raymond Augé (@rotty3000)
Senior Software Architect Liferay, Inc. (@Liferay)
OSGi Fellow

Hallvard Trætteberg

unread,
Nov 3, 2020, 2:05:32 AM11/3/20
to bndtool...@googlegroups.com

Hi,

 

This corresponds to my mental model, too. Components are “resolved” during runtime when the bundles are activating. I believe you can in some sense move from the component axis to the bundle axis by declaring a capability, e.g. com.my.service.impl that the service implementation provides and that the service user requires. Without any bundle that provides the capability, the user of the service will not resolve. Is this correct?

 

Another thing I have noticed is that (at least Felix) allows a bundle to be active although a bundle it requires have been stopped. I created a single servlet bundle example and wanted to show the students how to split it in two with a domain bundle and a servlet bundle, where the servlet bundle imported a package that was exported from the domain bundle. I started the system and showed in the gogo console that they were both active. I then stopped the domain bundle, but the servlet bundle still showed up as active in the console. I couldn’t explain that to my students, and now I wonder how that could have happened?

 

Regards,

 

Hallvard

Peter Kriens

unread,
Nov 3, 2020, 2:57:42 AM11/3/20
to via bndtools-users
You probably are missing an 'implementation' requirement in your API bundle.

Your current situation is:

REST -> API 

So nobody needs the implementation, so everybody is happy. When you add an implementation dependency then you get:

REST -> API -> <implementation for API>

The API bundle would require implementation for API. Your provider bundle would then provide this capability.

However, you run head on in the optionality problem. Although the resolver can easily find _a_ provider if there are multiple? Which one should it choose? This selection of the provider is generally the task of the assembler. You want DB provider or an in memory provider? There is no way the resolver can know this.

As a side note. An alternative solution is to export the API package from the provider. In that case, the resolver will automatically drag in a provider since it is the only way to get the API, which is needed  by REST. In that case the API bundle should be blacklisted. This is currently frowned upon, and there was recently a discussion in the OSGi about it. There are pros and cons, one of the nice pros of export API is that systems tend to self-assemble. One of the pros of a separate API is that it is easier to test since you can easier stub or mock dependencies.

I once wrote a nice tutorial that discusses these issues: https://v2archive.enroute.osgi.org/tutorial_base/300-api.html

If you're starting with an in-house workspace, then my best advice is to have a single API project for all your projects in your workspace, it is forboten for two projects to talk to each other directly, only through an API defined in the central API project. The bundle from the API project is very useful to keep things together. It definitely makes testing easier since all package dependencies are resolved without dragging in other implementations.

If you do a project where you want to publish separate bundles to the world, I still like the export-API model. Nothing is as ugly as figuring out a large dependency tree. Felix recently broke out the Configurator in separate mandatory dependencies for JSON and I really dislike that model. In my mind a bundle should only depend on things that it cannot by function carry itself, e.g. shared things like Event Admin and Configuration Admin but not something as simple as a JSON parser. Every external dependency is a potential future conflict.

However, your mileage might vary who you ask.

Kind regards,

Peter Kriens












Jürgen Albert

unread,
Nov 3, 2020, 3:10:31 AM11/3/20
to bndtool...@googlegroups.com

Hi Hallvard,

This corresponds to my mental model, too. Components are “resolved” during runtime when the bundles are activating. I believe you can in some sense move from the component axis to the bundle axis by declaring a capability, e.g. com.my.service.impl that the service implementation provides and that the service user requires. Without any bundle that provides the capability, the user of the service will not resolve. Is this correct?

Kind of yes :-). The Magic is in the Requirement and Capability (https://docs.osgi.org/specification/osgi.core/7.0.0/framework.module.html#d0e2966). Besides the Req. and  Cap. that can be defined in the Manifest other Headers will be translated by the framework and for the resolver as well. An import package becomes a requirement for osgi.wiring.package=my.package e.g. exports will become provide capability. The moment someone tells the Framework to lift a bundle beyond the state of installed, it tries to resolve all requirements without an effective attribute or with effective:=resolve. With this the framework creates the wiring for all the defined osgi.wiring.* namespaces. This however this however only guarantees that the bundle will find all the classes it needs, when it gets started. (There are contracts and other requirements for the system, JRE etc. I just ignored for the sake of simplicity). The resolver should do exactly the same as the framework in its default configuration to provide a set of bundles that the framework finds satisfactory when it starts.

The Framework however does not care about services in this stage. It only guarantees that you will get no ClassNotFound Exception when it wants to have a Service of some Interface. This is where the ServiceLayer and SCR kicks in. The Moment a Bundle transitions to the state of Starting or Started SCR peeks into the Bundle and looks for component xmls. The XML allows SCR to know about the services your component needs. It then just sits and waits until all the mandatory services are around in order to start your Service/Component (There is much more to it, but this is the most simlyfied explanation I can come up with). What the component xml describes, bnd tries to add in a simplistic way to the Manifest in the form of Requirements and Capabilities for services as well. They have the attribute effective:=active and will be ignored by the framework. The resolver will ignore it too until you tell it not to, by declaring "-resolve.effective: active". Now the resolver will look for bundles that declare requirements and capabilities with this effective stage. This might never be a 100% fit for the component because it does not know about your target filters, but you will at least get a set of bundles that provide services of the kind you might need.

Another thing I have noticed is that (at least Felix) allows a bundle to be active although a bundle it requires have been stopped. I created a single servlet bundle example and wanted to show the students how to split it in two with a domain bundle and a servlet bundle, where the servlet bundle imported a package that was exported from the domain bundle. I started the system and showed in the gogo console that they were both active. I then stopped the domain bundle, but the servlet bundle still showed up as active in the console. I couldn’t explain that to my students, and now I wonder how that could have happened?

 

This is because of the already mentioned Bundle Wiring. The wiring describes where your bundle will find its classes and this is in effect as long as a bundle is at least in state resolved (https://docs.osgi.org/specification/osgi.core/7.0.0/framework.wiring.html#d0e13922). The Classloader simply does not care if the bundle is active. Active means for the Framework only that the BundleActivator has been called (if it has one). I had the same expectation at one time in the past, so this seems to be a common thing.

I hope this helps.

To view this discussion on the web visit https://groups.google.com/d/msgid/bndtools-users/409DD89E-2863-4939-81D5-9A10AF48C326%40gmail.com.
-- 
Jürgen Albert
Geschäftsführer

Data In Motion Consulting GmbH

Kahlaische Str. 4
07745 Jena

Mobil:  0157-72521634
E-Mail: j.al...@datainmotion.de
Web: www.datainmotion.de

XING:   https://www.xing.com/profile/Juergen_Albert5

Rechtliches

Jena HBR 513025

Hallvard Trætteberg

unread,
Nov 3, 2020, 2:52:53 PM11/3/20
to bndtool...@googlegroups.com

Jürgen,

 

Thanks for the clarification, particularly about how the wiring of a bundle to a required package does not require the providing bundle to be active, just resolved. I’m coming from Eclipse where you usually activate a bundle/plugin when a class in it is loaded, I think that’s where my misconception may come from, But it does really make sense! I saw Peter Kriens also mentioned 'implementation' requirement in an answer, which I guess a way of ensuring there is a service interface component provider.

 

Hallvard

Rubén Pérez Vázquez

unread,
Nov 5, 2020, 4:28:27 AM11/5/20
to bndtools-users
Hi all,

Sorry for the delayed answer. First of all, I need to correct myself when I said I did not get responses to my second email to this list. I had stupidly set the Google Groups configuration to "Do not send email", so I was getting no notice of the responses! I guess I should stop trying to figure out OSGi and go back to simpler things, like mailing list configuration :P

I am glad my question has sparkled such an interesting debate. I specially like the fact that someone has showed with a real life problem and that has prompted Jürgen to write his "OSGi resolution for dummies" answer. Very interesing! Thanks for that!

Now to go back to my original question: even though I did know that the bundle lifecycle and the DS lifecycle are separate, I was expecting (or rather: "hoping") there would be a way to prevent a bundle from activating until its DS requirements were satisfied (other than writing my own activator, of course). I've found Peter Kriens' answer particularly useful: the API requires an implementation and therefore nothing starts until an implementor bundle is present. That was basically what I was intuitively looking for.

I just have one doubt: when you say " This selection of the provider is generally the task of the assembler", you mean a "human" assembler, right? I mean, the person compiling the bundles to create an application needs to make sure that there is a provider to every API, and a provider that meets the requirements (for instance, an in memory provider may be suitable for a certain application, while another may require a proper persistence layer). If that is correct, the fact that the bundles consuming an API won't work until there is another bundle implementing such API certainly helps when assembling the application. I do not think that the problem of selecting a suitable provider can be automated, even though I might be wrong here.

Two final questions:
  • Is there a standard annotation to provide an implementation requirement?
  • Looking for examples of the implementation namespace in the Internet, I've seen it applied to classes consuming the API, but never to the API itself. What's the best practice here?

Again, thanks a lot for the helpful answers!

Best,
Rubén

Neil Bartlett

unread,
Nov 5, 2020, 5:11:18 AM11/5/20
to bndtool...@googlegroups.com
I think this sentence needs to be addressed:

> I was expecting (or rather: "hoping") there would be a way to prevent a bundle from activating until its DS requirements were satisfied

This is actually a bad thing to hope for. Bundle resolution happens earlier than DS component activation and must be oblivious of it. Think of it as a stack where the OSGi bundle resolution is a foundational level and the DS components are a higher level build on top. It would create circular dependencies if the lower level had a dependency on the higher level.

As an analogy, think about the layers of the OSI Network Model (https://en.wikipedia.org/wiki/OSI_model). What would happen if you tried to make your server's ethernet card (layer 1) power up *only* if the Apache Web Server (layer 7) started? Apache cannot start without a working ethernet interface, so you would be deadlocked.

This is why in OSGi I recommend starting ALL bundles at startup time of the application. During resolution and bundle start time, you cannot predict what bundles will want to do after they are started. This is not expensive... starting a bundle that doesn't have an activator is as cheap as flipping a single bit.

With regard to your questions:

> Is there a standard annotation to provide an implementation requirement?

Not in OSGi R7, you have to use the generic org.osgi.annotation.bundle.Capability annotation.

> Looking for examples of the implementation namespace in the Internet, I've seen it applied to classes consuming the API, but never to the API itself. What's the best practice here?

I don't know if there is an agreed best practice. Speaking for myself, I would not put an implementation requirement directly on the API but only on the consumer.

Neil


Rubén Pérez Vázquez

unread,
Nov 5, 2020, 6:43:12 AM11/5/20
to bndtool...@googlegroups.com
Hi Neil,

Thanks for the warning. That is a good analogy. My reflection came from the fact that DS seem to be such a central part of OSGi that it seems it does not make sense for a bundle to be active without their DS requirements. I totally understand they are in different levels and should not be coupled, but on the other hand, seeing that all bundles are active *but* no services are started is somewhat misleading. But don't get me wrong, I don't wish to break the beautiful OSGi model. If anything, I think it is the most elegant way to write and build applications in the Java world.

BTW, it's funny that I was reading this when I received your email: https://virtual.osgiusers.org/2018/10/pure-annotation-driven-dev#providing-and-requiring-capabilities :)

I think I'll go and create an annotation to require an implementation of the CompanyProvider and include it in my API bundle. Then I'll use it in the CompanyProvider consumer, that is, the CompanyService. I'll let you know how it goes.

Again, thanks a lot for the insightful answer. It's a pleasure to participate in such an active community!

Best regards,
Rubén

You received this message because you are subscribed to a topic in the Google Groups "bndtools-users" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/bndtools-users/NMLTSrnUt7o/unsubscribe.
To unsubscribe from this group and all its topics, send an email to bndtools-user...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/bndtools-users/CAPr%3D90Ou3y%2Bg3QUnj33Vs2qnDStJd50c9ZDuG39ae2UtPM6x5A%40mail.gmail.com.

Rubén Pérez Vázquez

unread,
Nov 5, 2020, 12:08:51 PM11/5/20
to bndtool...@googlegroups.com
Hi all,

So this is what I've got so far:
  • In the project org.example.company.provider.api, I have created the following "constants" class in package org.example.company.provider, which is not exported:
    package org.example.company.provider;

    public final class CompanyProviderConstants {

      private CompanyProviderConstants() {
        // Not instantiable
      }

       public static final String PACKAGE_VERSION = "1.0.0";
       public static final String CAPABILITY_NAME = "org.example.company.provider";
       public static final String CAPABILITY_VERSION = "1.0";

    }

  •  In the same project, under the org.example.company.provider.api package (exported), there is the CompanyProvider interface:
  • package org.example.company.provider.api;

    import java.util.List;

    import org.osgi.annotation.versioning.ProviderType;

    import org.example.company.Company;
  • import org.example.company.CompanyAlreadyExistsException;
    import org.example.company.NoSuchCompanyException;


  • @ProviderType
    public interface CompanyProvider {
      public void add(Company company) throws CompanyAlreadyExistsException;
      public Company get(String name) throws NoSuchCompanyException;
      public void update(Company company) throws NoSuchCompanyException;
  •   public void delete(String name) throws NoSuchCompanyException;
      public List<Company> getAll();
    }

    , the ProvideCompanyProvider annotation:
    package org.example.company.provider.api;

    import static java.lang.annotation.ElementType.PACKAGE;
    import static java.lang.annotation.ElementType.TYPE;

    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;

    import org.osgi.annotation.bundle.Capability;
    import org.osgi.namespace.implementation.ImplementationNamespace;

    import org.example.company.provider.CompanyProviderConstants;

    @Capability(
      namespace = ImplementationNamespace.IMPLEMENTATION_NAMESPACE,
      name = CompanyProviderConstants.CAPABILITY_NAME, version = CompanyProviderConstants.CAPABILITY_VERSION)
    @Target({ TYPE, PACKAGE })
    @Retention(RetentionPolicy.CLASS)
    public @interface ProvideCompanyProvider {}

    , the RequireCompanyProvider annotation:
    package org.example.company.provider.api;

    import static java.lang.annotation.ElementType.PACKAGE;
    import static java.lang.annotation.ElementType.TYPE;

    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;

    import org.osgi.annotation.bundle.Requirement;
    import org.osgi.namespace.implementation.ImplementationNamespace;

    import org.example.company.provider.CompanyProviderConstants;

    @Requirement(
      namespace = ImplementationNamespace.IMPLEMENTATION_NAMESPACE,
      name = CompanyProviderConstants.CAPABILITY_NAME, version = CompanyProviderConstants.CAPABILITY_VERSION)
    @Target({ TYPE, PACKAGE })
    @Retention(RetentionPolicy.CLASS)
    public @interface RequireCompanyProvider {}

    and the package-info.java file:
    @org.osgi.annotation.bundle.Export
    @org.osgi.annotation.versioning.Version(CompanyProviderConstants.PACKAGE_VERSION)
    package org.example.company.provider.api;

    import org.example.company.provider.CompanyProviderConstants;
  • In the project org.example.company.provider.inmemory.impl I have annotated the existing InMemoryCompanyProvider class as follows:
    @Component(scope = ServiceScope.SINGLETON)
    @ProvideCompanyProvider

  • public class InMemoryCompanyProvider implements CompanyProvider {

  • And, finally, the project org.example.company.service.simple.impl contains the CompanyServiceSimpleImpl with the following annotations:
    @Component(service = CompanyService.class, scope = ServiceScope.SINGLETON)
    @JaxrsResource
    @RequireCompanyProvider

  • public class CompanyServiceSimpleImpl implements CompanyService {
With this setup, if I include the org.example.company.service.simple.impl (and only that one package), the resolver automatically includes the bundles containing the CompanyProvider API and implementation, which is kind of what I was expecting. Also, if I remove the provider implementation bundle, the service goes to an "Installed" state, since it is missing the required capability.

And, as Peter Kriens said, I've "run head on in the optionality problem".

What if I now define a JPA-based CompanyProvider and I annotate with @ProvideCompanyProvider? Which one will bnd likely pull? Just one or all the matching implementations it can find in the workspace?

Thanks!

Rubén

Jürgen Albert

unread,
Nov 6, 2020, 3:01:15 AM11/6/20
to bndtool...@googlegroups.com

Hi,

before I answer I'd like to recap and confirm what I understand you want to do:

* You have one or many bundles that provide components that reference a service of type CompanyProvider
* CompanyProvider resides in a Bundle that only contains the API but no implementation
* There may be multiple implementations but at least one implementation must be present, when someone Requires a service of this kind

Is this correct?

Besides this: If you provide your own requirements and capabilities, create your own namespace. The ImplementationNamespace should only be used for Spec and Contract implementations. See: https://docs.osgi.org/specification/osgi.cmpn/7.0.0/service.namespaces.html#service.namespaces-osgi.implementation.namespace

Rubén Pérez Vázquez

unread,
Nov 6, 2020, 4:05:08 AM11/6/20
to bndtool...@googlegroups.com
Dear Jürgen,

Please find my answers inline. Danke schön! ;)

Best regards,
Rubén

El vie., 6 nov. 2020 a las 9:01, Jürgen Albert (<j.al...@data-in-motion.biz>) escribió:

Hi,

before I answer I'd like to recap and confirm what I understand you want to do:

* You have one or many bundles that provide components that reference a service of type CompanyProvider

Well, no, I just have one so far, but I am kind of experimenting. My previous email asked about the best practice of separating the API in its own bundle, which led me to bundles active that did nothing because there were no implementations in place.
With the current approach, bundles only start when at least a Provider implementation is in place (which may or may not be a good thing -- as I said, I'm just experimenting--). Now I'm just curious to know what would the resolver do if there were several classes implementing the CompanyProvider and marked with the @ProvideCompanyProvider annotation.

* CompanyProvider resides in a Bundle that only contains the API but no implementation

Yes, that is so. I basically described it in my previous email. There are no other files apart from those I mentioned.

* There may be multiple implementations but at least one implementation must be present, when someone Requires a service of this kind

Yes, that is exactly so

Is this correct?

Besides this: If you provide your own requirements and capabilities, create your own namespace. The ImplementationNamespace should only be used for Spec and Contract implementations. See: https://docs.osgi.org/specification/osgi.cmpn/7.0.0/service.namespaces.html#service.namespaces-osgi.implementation.namespace

Well, I assumed I could somehow "namespace" the capability/requirement name. Should I then require/provide a capability of "company.provider" inside the "org.example" namespace, instead of an "org.example.company.provider" inside the implementation namespace?

Jürgen Albert

unread,
Nov 6, 2020, 4:21:47 AM11/6/20
to bndtool...@googlegroups.com

Hi,

if you really really want that your API always comes along with an implementation you have two possibilities:

A. Just put both in one Bundle and only export the API.
B. If you want to have API and Impl separated, you are on the right track with your solution. You can have a look here: https://gitlab.com/gecko.io/geckoMongoEMF/-/blob/develop/org.gecko.emf.mongo/src/org/gecko/emf/mongo/package-info.java. We are doing exactly this.

Regarding the resolver: If you use component annotations you will see that bnd already creates requirements and capabilities for you. Just have a look at your generated jar. @Reference annotations will result in a corresponding Requirement for such a service, also marked as optional if the reference is not mandatory. Please note 2 things.

1. Component properties or target filters will not be reflected.
2. They are always effective=active.

The effective active causes the resolver and the framework to ignore these capabilities. Add "-resolver.effective: active" to your bndrun and now the resolver will look after them as well and thus pull in all required service implementations as well. Please note that optional requirements will end up in the optional part of the resolver result Dialogue. You need to manually mark them to pull them in as well.

Hope this helps,

Jürgen.

Reply all
Reply to author
Forward
0 new messages