Skip to content

Commit

Permalink
build: Drop OSGi support akka#28304
Browse files Browse the repository at this point in the history
  • Loading branch information
johanandren committed Nov 17, 2022
1 parent a8ee6fa commit 6c9f781
Show file tree
Hide file tree
Showing 20 changed files with 5 additions and 1,141 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ akka-docs-dev/rst_preprocessed/
akka-docs/_build/
akka-docs/exts/
akka-docs/rst_preprocessed/
akka-osgi/src/main/resources/*.conf
akka.sublime-project
akka.sublime-workspace
akka.tmproj
Expand Down
1 change: 0 additions & 1 deletion akka-actor/src/main/scala/akka/actor/ActorSystem.scala
Original file line number Diff line number Diff line change
Expand Up @@ -997,7 +997,6 @@ private[akka] class ActorSystemImpl(
"akka-distributed-data",
"akka-testkit",
"akka-multi-node-testkit",
"akka-osgi",
"akka-persistence",
"akka-persistence-query",
"akka-persistence-shared",
Expand Down
116 changes: 3 additions & 113 deletions akka-docs/src/main/paradox/additional/osgi.md
Original file line number Diff line number Diff line change
@@ -1,116 +1,6 @@
# Akka in OSGi

## Dependency
OSGi packaging and support in Akka has been dropped. Last artifacts with OSGi support was Akka 2.8.0.

To use Akka in OSGi, you must add the following dependency in your project:

@@dependency[sbt,Maven,Gradle] {
bomGroup=com.typesafe.akka bomArtifact=akka-bom_$scala.binary.version$ bomVersionSymbols=AkkaVersion
symbol1=AkkaVersion
value1="$akka.version$"
group=com.typesafe.akka
artifact=akka-osgi_$scala.binary.version$
version=AkkaVersion
}

## Background

[OSGi](https://www.osgi.org/resources/where-to-start/) is a mature packaging and deployment standard for component-based systems. It
has similar capabilities as [Project Jigsaw](https://openjdk.java.net/projects/jigsaw/) (originally scheduled for JDK 1.8), but has far stronger facilities to
support legacy Java code. This is to say that while Jigsaw-ready modules require significant changes to most source files
and on occasion to the structure of the overall application, OSGi can be used to modularize almost any Java code as far
back as JDK 1.2, usually with no changes at all to the binaries.

These legacy capabilities are OSGi's major strength and its major weakness. The creators of OSGi realized early on that
implementors would be unlikely to rush to support OSGi metadata in existing JARs. There were already a handful of new
concepts to learn in the JRE and the added value to teams that were managing well with straight J2EE was not obvious.
Facilities emerged to "wrap" binary JARs so they could be used as bundles, but this functionality was only used in limited
situations. An application of the "80/20 Rule" here would have that "80% of the complexity is with 20% of the configuration",
but it was enough to give OSGi a reputation that has stuck with it to this day.

This document aims to the productivity basics folks need to use it with Akka, the 20% that users need to get 80% of what they want.
For more information than is provided here, [OSGi In Action](https://www.manning.com/books/osgi-in-action) is worth exploring.

## Core Components and Structure of OSGi Applications

The fundamental unit of deployment in OSGi is the `Bundle`. A bundle is a Java JAR with *additional
entries <https://docs.osgi.org/reference/bundle-headers.html>* in `MANIFEST.MF` that minimally expose the name and version
of the bundle and packages for import and export. Since these manifest entries are ignored outside OSGi deployments,
a bundle can interchangeably be used as a JAR in the JRE.

When a bundle is loaded, a specialized implementation of the Java @javadoc[ClassLoader](java.lang.ClassLoader) is instantiated for each bundle. Each
classloader reads the manifest entries and publishes both capabilities (in the form of the `Bundle-Exports`) and
requirements (as `Bundle-Imports`) in a container singleton for discovery by other bundles. The process of matching imports to
exports across bundles through these classloaders is the process of resolution, one of six discrete steps in the lifecycle
FSM of a bundle in an OSGi container:

1. INSTALLED: A bundle that is installed has been loaded from disk and a classloader instantiated with its capabilities.
Bundles are iteratively installed manually or through container-specific descriptors. For those familiar with legacy packging
such as EJB, the modular nature of OSGi means that bundles may be used by multiple applications with overlapping dependencies.
By resolving them individually from repositories, these overlaps can be de-duplicated across multiple deployments to
the same container.
2. RESOLVED: A bundle that has been resolved is one that has had its requirements (imports) satisfied. Resolution does
mean that a bundle can be started.
3. STARTING: A bundle that is started can be used by other bundles. For an otherwise complete application closure of
resolved bundles, the implication here is they must be started in the order directed by a depth-first search for all to
be started. When a bundle is starting, any exposed lifecycle interfaces in the bundle are called, giving the bundle
the opportunity to start its own service endpoints and threads.
4. ACTIVE: Once a bundle's lifecycle interfaces return without error, a bundle is marked as active.
5. STOPPING: A bundle that is stopping is in the process of calling the bundle's stop lifecycle and transitions back to
the RESOLVED state when complete. Any long running services or threads that were created while STARTING should be shut
down when the bundle's stop lifecycle is called.
6. UNINSTALLED: A bundle can only transition to this state from the INSTALLED state, meaning it cannot be uninstalled
before it is stopped.

Note the dependency in this FSM on lifecycle interfaces. While there is no requirement that a bundle publishes these
interfaces or accepts such callbacks, the lifecycle interfaces provide the semantics of a `main()` method and allow
the bundle to start and stop long-running services such as REST web services, ActorSystems, Clusters, etc.

Secondly, note when considering requirements and capabilities, it's a common misconception to equate these with repository
dependencies as might be found in Maven or Ivy. While they provide similar practical functionality, OSGi has several
parallel type of dependency (such as Blueprint Services) that cannot be easily mapped to repository capabilities. In fact,
the core specification leaves these facilities up to the container in use. In turn, some containers have tooling to generate
application load descriptors from repository metadata.

## Notable Behavior Changes

Combined with understanding the bundle lifecycle, the OSGi developer must pay attention to sometimes unexpected behaviors
that are introduced. These are generally within the JVM specification, but are unexpected and can lead to frustration.

*
Bundles should not export overlapping package spaces. It is not uncommon for legacy JVM frameworks to expect plugins
in an application composed of multiple JARs to reside under a single package name. For example, a frontend application
might scan all classes from `com.example.plugins` for specific service implementations with that package existing in
several contributed JARs.
While it is possible to support overlapping packages with complex manifest headers, it's much better to use non-overlapping
package spaces and facilities such as @ref:[Akka Cluster](../typed/cluster-concepts.md)
for service discovery. Stylistically, many organizations opt to use the root package path as the name of the bundle
distribution file.

* Resources are not shared across bundles unless they are explicitly exported, as with classes. The common
case of this is expecting that `getClass().getClassLoader().getResources("foo")` will return all files on the classpath
named `foo`. The @javadoc[getResources()](java.lang.ClassLoader#getResources(java.lang.String)) method only returns resources from the current classloader, and since there are
separate classloaders for every bundle, resource files such as configurations are no longer searchable in this manner.

## Configuring the OSGi Framework

To use Akka in an OSGi environment, the container must be configured such that the `org.osgi.framework.bootdelegation`
property delegates the `sun.misc` package to the boot classloader instead of resolving it through the normal OSGi class space.

## Intended Use

Akka only supports the usage of an ActorSystem strictly confined to a single OSGi bundle, where that bundle contains or imports
all of the actor system's requirements. This means that the approach of offering an ActorSystem as a service to which Actors
can be deployed dynamically via other bundles is not recommended — an ActorSystem and its contained actors are not meant to be
dynamic in this way. ActorRefs may safely be exposed to other bundles.

## Activator

To bootstrap Akka inside an OSGi environment, you can use the @apidoc[akka.osgi.ActorSystemActivator](akka.osgi.ActorSystemActivator) class
to conveniently set up the @apidoc[ActorSystem](akka.actor.ActorSystem).

@@snip [Activator.scala](/akka-osgi/src/test/scala/docs/osgi/Activator.scala) { #Activator }

The goal here is to map the OSGi lifecycle more directly to the Akka lifecycle. The @apidoc[ActorSystemActivator](akka.osgi.ActorSystemActivator) creates
the actor system with a class loader that finds resources (`application.conf` and `reference.conf` files) and classes
from the application bundle and all transitive dependencies.
Projects using OSGi will need to re-package or find another solution for using OSGi, you can find some possible hints
in issue: https://github.com/akka/akka/issues/28304
135 changes: 0 additions & 135 deletions akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala

This file was deleted.

100 changes: 0 additions & 100 deletions akka-osgi/src/main/scala/akka/osgi/BundleDelegatingClassLoader.scala

This file was deleted.

Loading

0 comments on commit 6c9f781

Please sign in to comment.