{"id":23293852,"url":"https://github.com/l7r7/magic-bundle","last_synced_at":"2025-04-06T18:49:22.064Z","repository":{"id":130028955,"uuid":"83655682","full_name":"L7R7/magic-bundle","owner":"L7R7","description":"OSGi experiments","archived":false,"fork":false,"pushed_at":"2017-06-21T14:13:55.000Z","size":134,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-02-13T00:47:52.963Z","etag":null,"topics":["breakable-toy","java","magic","osgi","osgi-services"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/L7R7.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-03-02T08:48:48.000Z","updated_at":"2020-10-05T01:58:26.000Z","dependencies_parsed_at":"2023-07-11T15:16:10.827Z","dependency_job_id":null,"html_url":"https://github.com/L7R7/magic-bundle","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/L7R7%2Fmagic-bundle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/L7R7%2Fmagic-bundle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/L7R7%2Fmagic-bundle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/L7R7%2Fmagic-bundle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/L7R7","download_url":"https://codeload.github.com/L7R7/magic-bundle/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247535486,"owners_count":20954574,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["breakable-toy","java","magic","osgi","osgi-services"],"created_at":"2024-12-20T06:17:48.115Z","updated_at":"2025-04-06T18:49:22.036Z","avatar_url":"https://github.com/L7R7.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# magic-bundle\nThis project contains a couple of samples for usages of different OSGi concepts and technologies, most of them in relation to consuming services provided by another bundle.\nIt is intended to be used as a breakable toy in order to understand the magic OSGi uses for providing its functionality.\n\n## Concepts covered\nCurrently there are examples for the following classes/concepts:\n* [`ServiceTracker`](https://osgi.org/javadoc/r4v42/org/osgi/util/tracker/ServiceTracker.html)\n* [`ServiceListener`](https://osgi.org/javadoc/r4v43/core/org/osgi/framework/ServiceListener.html)\n* [`ServiceTrackerCustomizer`](https://osgi.org/javadoc/r4v42/org/osgi/util/tracker/ServiceTrackerCustomizer.html) (in combination with the `ServiceTracker`)\n* Blueprint\n\n## Provided services\nTo be able to see the stuff in action, there are two services that can be consumed:\n\n* A [`RandomNumberGenerator`](https://github.com/L7R7/magic-bundle/blob/master/service-number-api/src/main/java/de/l7r7/proto/bundle/magic/number/api/RandomNumberGenerator.java) that generates a random integer value\n* A [`RandomStringGenerator`](https://github.com/L7R7/magic-bundle/blob/master/service-string-api/src/main/java/de/l7r7/proto/bundle/magic/string/api/RandomStringGenerator.java) that generates a string containing a random Gaussian value\n\nEach of the two services consists of two bundles, one with the API that will be consumed by others and another one containing the actual service implementation (which should be hidden from the consumers)\n\n## Project structure\n\n### General\nThe bundle structure is represented in the Gradle structure of the project: Each bundle gets its own module.\nTo simplify the addition of new bundles (in particular the generation of a `build.gradle` file), there's the [`osgi.gradle`](https://github.com/L7R7/magic-bundle/blob/master/gradle/osgi.gradle) file that includes the basic things needed in order to make a Gradle module being deployed to the OSGi container.\nThe main benefit of the file is that the `build.gradle` file of a bundle will look like this (assuming there is nothing like additional dependencies):\n\n```groovy\ngroup 'de.l7r7.proto'\nversion '0.1'\n\napply from: \"$rootDir/gradle/osgi.gradle\"\n```\n\nThis is not the most elegant solution and should probably not be used directly in any production project, but it allows the fast creation of new bundles without much overhead.\n\n### Gradle modules\n* **consumer-bean-initialization:** This example combines the usage of blueprint to get two service instances (namely a `RandomNumberGenerator` and a `RandomStringGenerator`) and as an IoC container to provide objects for a field.\nThe object provided to the `Main` class is the `StringConcatenator` which itself needs a `StringToLower` object. `StringToLower` in turn needs a `StringProvider` (Yes, the things these three classes do are pretty stupid but they serve the purpose of showing how to use blueprint for dependency injection).\n* **consumer-blueprint-number:** A `RandomNumberGenerator` service is provided via blueprint\n* **consumer-blueprint-string:** A `RandomStringGenerator` service is provided via blueprint\n* **consumer-listener:** The bundle's Activator will register a `ServiceListener` to get a `RandomNumberGenerator` service instance.\nThe problem with this approach is, that it won't get a service that is present before the bundle itself is started.\n* **consumer-listener-tracker:** This bundle combines a `ServiceTracker` and a `ServiceListener` to get a `RandomNumberGenerator` service instance.\nBasically, this bundle combines the functionality of the *consumer-tracker* bundle with the *consumer-listener* bundle.\n* **consumer-multi-service:** *not implemented yet*\n* **consumer-pretty-listening-tracker:** This bundle does the same thing as the *consumer-listener-tracker* bundle encapsulates the OSGi complexity in a utility class called [`CustomGenericDefaultServiceObservingProvidility`](https://github.com/L7R7/magic-bundle/blob/master/consumer-util/src/main/java/de/l7r7/proto/bundle/magic/util/CustomGenericDefaultServiceObservingProvidility.java). (Don't take this too serious :wink:)\n* **consumer-tracker:** The bundle's Activator will use a `ServiceTracker` to get a `RandomNumberGenerator` service instance.\nThe caveat here is that the tracker isn't capable of handling services that appear and disappear at runtime with this implementation.\n* **consumer-tracker-customizer:** This bundle provides a `RandomNumberGenerator` service instance by using a `ServiceTracker` with a `ServiceTrackerCustomizer`.\nWith the Customizer it is possible to handle services that come and go at runtime.\nThe `ServiceTrackerCustomizer` seems to be the intended way to handle dynamic service instances properly (besides Blueprint)\n* **consumer-tracker-customizer-filter:** This bundle makes use of the same basic principle as the consumer-tracker-customizer but the `ServiceTracker` is tracking both the `RandomNumberGenerator` and the `RandomStringGenerator` by specifying a [`Filter`](https://osgi.org/javadoc/r4v42/org/osgi/framework/Filter.html) for the `ServiceTracker`.\nThe services are only consumed if both of them are available (this assumption adds some more complexity and it is done on purpose to simulate a real use case).\nSince the two services are of different types, the tracker (and the customizer of course) have to be more generic to be able to deal with the different types.\nIn the example, the common type of the two services is `Object`.\nThis requires a `instanceof` check every time a service appears or disappears.\nApart from that a further check is necessary whenever a `ServiceReference` is used: A `ServiceReference` is generic and wraps a service instance of a certain type.\nDue to the nature of the JVM, the type of a Generic object can't be determined at runtime.\nTo get the type (more specifically the class name) of the referenced service, the `ServiceReference` has a property that can be accessed like this: `String objectClass = ((String[]) reference.getProperty(Constants.OBJECTCLASS))[0];`\u003csup id=\"a1\"\u003e[1](#f1)\u003c/sup\u003e\nAn alternative approach for the problem of having to track multiple services is to use an individual `ServiceTracker` for each service.\nHowever, since there has to be a `ServiceTrackerCustomizer` for each tracker, the code becomes confusing pretty quickly. This approach becomes much more complicated when the number of services increases. \nAs a rule of thumb: If there are more than two services (and especially if all of them are required), use a Filter. Otherwise use individual trackers.\n* **consumer-util:** This bundle contains the [`CustomGenericDefaultServiceObservingProvidility`](https://github.com/L7R7/magic-bundle/blob/master/consumer-util/src/main/java/de/l7r7/proto/bundle/magic/util/CustomGenericDefaultServiceObservingProvidility.java) class used by the consumer-pretty-listening-tracker bundle.\n* **service-number-api:** This bundle contains the interface for the `RandomNumberGenerator`.\nThe service interfaces are separated from their implementation to keep them stable and to hide implementation details.\n* **service-number-impl:** This bundle contains the implementation of the `RandomNumberGenerator` interface. The wiring is done using blueprint.\n* **service-string-api:** This bundle contains the interface for the `RandomStringGenerator`.\n* **service-string-impl:** This bundle contains the implementation of the `RandomStringGenerator` interface. As in the service-number-impl bundle, the implementation is bound to the interface by using blueprint. \n* **servlet-filter:** Here you can find an approach on registering a servlet filter to a servlet.\n\n## So what's the learning?\n* Blueprint will provide service implementations after the bundle has started.\nThe order in which services will appear is non-deterministic and it could be that the bundle has to run for a while before all the services are present (even if the services are present when the bundle is started).\nAfter making a application ready for this, things like null-checks will be all over the place (null-checks are never a bad idea in OSGi anyways).\n* In contrast to the blueprint approach, with the `ServiceTracker` it is possible to get the (existing) services before the bundle reaches its \"started\" state.\n However, if you want to be safe against dynamic services, you have to add a `ServiceTrackerCustomizer` which roughly adds the functionality of a `ServiceListener` to the tracker. (An alternative is the Providility class :wink:)\n* To get the type of the service a `ServiceReference` is referring to, you can get the property with the key \"objectClass\" (or even better: use [`Constants.OBJECTCLASS`](https://osgi.org/javadoc/r4v43/core/org/osgi/framework/Constants.html#OBJECTCLASS) from the `org.osgi.framework` package).\nThis will return an *array(!)* of strings containing the class names of the referenced service. \n\n## Contributing\nIf you want to play around with the examples, feel free to fork the project. Pull requests are highly appreciated as well.\nIf there are any questions or problems, open an issue or [ping me on Twitter](https://twitter.com/l7r7_)\n\n---\n\n\u003cb id=\"f1\"\u003e1\u003c/b\u003e At the moment I'm not sure if there is a case where the array of objectClasses contains more than one element. [↩](#a1)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fl7r7%2Fmagic-bundle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fl7r7%2Fmagic-bundle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fl7r7%2Fmagic-bundle/lists"}