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();
}
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();
}
}
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());
}
}
--
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.
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
To view this discussion on the web visit https://groups.google.com/d/msgid/bndtools-users/CAMm6HcBytZOXk60Oexkcuhthv2uuz6efE_8VE1cFXQVCbU3X%3Dg%40mail.gmail.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
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
To view this discussion on the web visit https://groups.google.com/d/msgid/bndtools-users/c10b2145-3495-de73-0f68-10c7170e5fb8%40data-in-motion.biz.
To view this discussion on the web visit https://groups.google.com/d/msgid/bndtools-users/0e1501d4-6ef8-49ad-b144-af37fdb7d404n%40googlegroups.com.
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.
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";
}
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();
}
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 {}
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 {}
@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;
@Component(scope = ServiceScope.SINGLETON)
@ProvideCompanyProvider
public class InMemoryCompanyProvider implements CompanyProvider {
@Component(service = CompanyService.class, scope = ServiceScope.SINGLETON)
@JaxrsResource
@RequireCompanyProvider
public class CompanyServiceSimpleImpl implements CompanyService {
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
To view this discussion on the web visit https://groups.google.com/d/msgid/bndtools-users/CAFWAsK2udEHh0%3DR%3DwLDXQNNz4qdcR9UTuCu9gP0QqXzECEjhHg%40mail.gmail.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
To view this discussion on the web visit https://groups.google.com/d/msgid/bndtools-users/b7e7ce09-e679-3835-531b-feca5a28f0dd%40data-in-motion.biz.
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.
To view this discussion on the web visit https://groups.google.com/d/msgid/bndtools-users/CAFWAsK2PQNzLqdPffWJR%3D25ST-1ne%3D6H2yATPCC75qfNNQ%2BaJA%40mail.gmail.com.