Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[2054] fix OSGI support (manifest, class loading) #2081

Merged
merged 3 commits into from
Dec 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
69 changes: 65 additions & 4 deletions liquibase-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@
<version>1.3</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
<scope>provided</scope>
<version>4.3.1</version>
</dependency>
<version>5.0.0</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
Expand Down Expand Up @@ -155,10 +155,11 @@
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>3.3.0</version>
<version>5.1.2</version>
<configuration>
<instructions>
<Bundle-SymbolicName>org.liquibase.core</Bundle-SymbolicName>
<Bundle-Activator>liquibase.osgi.Activator</Bundle-Activator>
<Import-Package>
javax.activation*;resolution:=optional,
javax.servlet.*;version="[2.6,4)";resolution:=optional,
Expand All @@ -167,6 +168,66 @@
org.yaml.snakeyaml.*,
*;resolution:=optional
</Import-Package>
<Provide-Capability>
osgi.serviceloader; osgi.serviceloader=liquibase.serializer.ChangeLogSerializer,
osgi.serviceloader; osgi.serviceloader=liquibase.parser.NamespaceDetails,
osgi.serviceloader; osgi.serviceloader=liquibase.database.Database,
osgi.serviceloader; osgi.serviceloader=liquibase.change.Change,
osgi.serviceloader; osgi.serviceloader=liquibase.database.DatabaseConnection,
osgi.serviceloader; osgi.serviceloader=liquibase.precondition.Precondition,
osgi.serviceloader; osgi.serviceloader=liquibase.serializer.SnapshotSerializer,
osgi.serviceloader; osgi.serviceloader=liquibase.configuration.AutoloadedConfigurations,
osgi.serviceloader; osgi.serviceloader=liquibase.diff.DiffGenerator,
osgi.serviceloader; osgi.serviceloader=liquibase.lockservice.LockService,
osgi.serviceloader; osgi.serviceloader=liquibase.changelog.ChangeLogHistoryService,
osgi.serviceloader; osgi.serviceloader=liquibase.datatype.LiquibaseDataType,
osgi.serviceloader; osgi.serviceloader=liquibase.configuration.ConfigurationValueProvider,
osgi.serviceloader; osgi.serviceloader=liquibase.logging.LogService,
osgi.serviceloader; osgi.serviceloader=liquibase.snapshot.SnapshotGenerator,
osgi.serviceloader; osgi.serviceloader=liquibase.parser.ChangeLogParser,
osgi.serviceloader; osgi.serviceloader=liquibase.servicelocator.ServiceLocator,
osgi.serviceloader; osgi.serviceloader=liquibase.diff.compare.DatabaseObjectComparator,
osgi.serviceloader; osgi.serviceloader=liquibase.command.LiquibaseCommand,
osgi.serviceloader; osgi.serviceloader=liquibase.license.LicenseService,
osgi.serviceloader; osgi.serviceloader=liquibase.diff.output.changelog.ChangeGenerator,
osgi.serviceloader; osgi.serviceloader=liquibase.executor.Executor,
osgi.serviceloader; osgi.serviceloader=liquibase.structure.DatabaseObject,
osgi.serviceloader; osgi.serviceloader=liquibase.parser.SnapshotParser,
osgi.serviceloader; osgi.serviceloader=liquibase.hub.HubService,
osgi.serviceloader; osgi.serviceloader=liquibase.command.CommandStep,
osgi.serviceloader; osgi.serviceloader=liquibase.sqlgenerator.SqlGenerator
</Provide-Capability>
<Require-Capability>
osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)",
osgi.extender; filter:="(osgi.extender=osgi.serviceloader.processor)",
osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.serializer.ChangeLogSerializer)"; cardinality:=multiple,
osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.parser.NamespaceDetails)"; cardinality:=multiple,
osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.database.Database)"; cardinality:=multiple,
osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.change.Change)"; cardinality:=multiple,
osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.database.DatabaseConnection)"; cardinality:=multiple,
osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.precondition.Precondition)"; cardinality:=multiple,
osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.serializer.SnapshotSerializer)"; cardinality:=multiple,
osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.configuration.AutoloadedConfigurations)"; cardinality:=multiple,
osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.diff.DiffGenerator)"; cardinality:=multiple,
osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.lockservice.LockService)"; cardinality:=multiple,
osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.changelog.ChangeLogHistoryService)"; cardinality:=multiple,
osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.datatype.LiquibaseDataType)"; cardinality:=multiple,
osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.configuration.ConfigurationValueProvider)"; cardinality:=multiple,
osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.logging.LogService)"; cardinality:=multiple,
osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.snapshot.SnapshotGenerator)"; cardinality:=multiple,
osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.parser.ChangeLogParser)"; cardinality:=multiple,
osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.servicelocator.ServiceLocator)"; cardinality:=multiple,
osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.diff.compare.DatabaseObjectComparator)"; cardinality:=multiple,
osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.command.LiquibaseCommand)"; cardinality:=multiple,
osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.license.LicenseService)"; cardinality:=multiple,
osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.diff.output.changelog.ChangeGenerator)"; cardinality:=multiple,
osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.executor.Executor)"; cardinality:=multiple,
osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.structure.DatabaseObject)"; cardinality:=multiple,
osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.parser.SnapshotParser)"; cardinality:=multiple,
osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.hub.HubService)"; cardinality:=multiple,
osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.command.CommandStep)"; cardinality:=multiple,
osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.sqlgenerator.SqlGenerator)"; cardinality:=multiple
</Require-Capability>
</instructions>
</configuration>
<executions>
Expand Down
3 changes: 3 additions & 0 deletions liquibase-core/src/main/java/liquibase/Scope.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import liquibase.logging.Logger;
import liquibase.logging.core.JavaLogService;
import liquibase.logging.core.LogServiceFactory;
import liquibase.osgi.Activator;
import liquibase.resource.ClassLoaderResourceAccessor;
import liquibase.resource.ResourceAccessor;
import liquibase.servicelocator.ServiceLocator;
Expand Down Expand Up @@ -55,6 +56,7 @@ public enum Attr {
fileEncoding,
databaseChangeLog,
changeSet,
osgiPlatform
}

private static ScopeManager scopeManager;
Expand Down Expand Up @@ -95,6 +97,7 @@ public static Scope getCurrentScope() {
}

rootScope.values.put(Attr.serviceLocator.name(), serviceLocator);
rootScope.values.put(Attr.osgiPlatform.name(), Activator.OSGIContainerChecker.isOsgiPlatform());
}
return scopeManager.getCurrentScope();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import liquibase.util.ObjectUtil;

import java.util.*;
import liquibase.util.OsgiUtil;

/**
* Adapts CustomChange implementations to the standard change system used by Liquibase.
Expand Down Expand Up @@ -69,16 +70,21 @@ public CustomChangeWrapper setClass(String className) throws CustomChangeExcepti
return this;
}
this.className = className;
try {
try {
Boolean osgiPlatform = Scope.getCurrentScope().get(Scope.Attr.osgiPlatform, Boolean.class);
if (Boolean.TRUE.equals(osgiPlatform)) {
customChange = (CustomChange)OsgiUtil.loadClass(className).getConstructor().newInstance();
} else {
try {
customChange = (CustomChange) Class.forName(className, true, Scope.getCurrentScope().getClassLoader()).getConstructor().newInstance();
} catch (ClassCastException e) { //fails in Ant in particular
try {
customChange = (CustomChange) Class.forName(className, true, Scope.getCurrentScope().getClassLoader()).getConstructor().newInstance();
} catch (ClassCastException e) { //fails in Ant in particular
try {
customChange = (CustomChange) Thread.currentThread().getContextClassLoader().loadClass(className).getConstructor().newInstance();
} catch (ClassNotFoundException e1) {
customChange = (CustomChange) Class.forName(className).getConstructor().newInstance();
}
customChange = (CustomChange) Thread.currentThread().getContextClassLoader().loadClass(className).getConstructor().newInstance();
} catch (ClassNotFoundException e1) {
customChange = (CustomChange) Class.forName(className).getConstructor().newInstance();
}
}
}
} catch (Exception e) {
throw new CustomChangeException(e);
}
Expand Down Expand Up @@ -316,7 +322,12 @@ public void customLoadLogic(ParsedNode parsedNode, ResourceAccessor resourceAcce

CustomChange customChange = null;
try {
customChange = (CustomChange) Class.forName(className, false, Scope.getCurrentScope().getClassLoader()).getConstructor().newInstance();
Boolean osgiPlatform = Scope.getCurrentScope().get(Scope.Attr.osgiPlatform, Boolean.class);
if (Boolean.TRUE.equals(osgiPlatform)) {
customChange = (CustomChange)OsgiUtil.loadClass(className).getConstructor().newInstance();
} else {
customChange = (CustomChange) Class.forName(className, false, Scope.getCurrentScope().getClassLoader()).getConstructor().newInstance();
}
} catch (Exception e) {
throw new UnexpectedLiquibaseException(e);
}
Expand Down
108 changes: 108 additions & 0 deletions liquibase-core/src/main/java/liquibase/osgi/Activator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package liquibase.osgi;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import liquibase.osgi.Activator.LiquibaseBundle;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.util.tracker.BundleTracker;
import org.osgi.util.tracker.BundleTrackerCustomizer;

public class Activator implements BundleActivator, BundleTrackerCustomizer<LiquibaseBundle> {

private static final String LIQUIBASE_CUSTOM_CHANGE_WRAPPER_PACKAGES = "Liquibase-Custom-Change-Packages";
private BundleTracker<LiquibaseBundle> bundleTracker;
private static final List<LiquibaseBundle> liquibaseBundles = new CopyOnWriteArrayList<>();

@Override
public void start(final BundleContext bc) throws Exception {
OSGIContainerChecker.osgiPlatform();
bundleTracker = new BundleTracker<>(bc, Bundle.ACTIVE, this);
bundleTracker.open();
}

@Override
public void stop(BundleContext context) throws Exception {
bundleTracker.close();
liquibaseBundles.clear();
}

public static List<LiquibaseBundle> getLiquibaseBundles() {
return Collections.unmodifiableList(liquibaseBundles);
}

@Override
public LiquibaseBundle addingBundle(Bundle bundle, BundleEvent event) {
if (bundle.getBundleId() == 0) {
return null;
}
String customWrapperPackages = (String) bundle.getHeaders().get(LIQUIBASE_CUSTOM_CHANGE_WRAPPER_PACKAGES);
if (customWrapperPackages != null) {
LiquibaseBundle lb = new LiquibaseBundle(bundle, customWrapperPackages);
liquibaseBundles.add(lb);
return lb;
}
return null;
}

@Override
public void modifiedBundle(Bundle bundle, BundleEvent event, LiquibaseBundle liquibaseBundle) {
// nothing to do
}

@Override
public void removedBundle(Bundle bundle, BundleEvent event, LiquibaseBundle liquibaseBundle) {
if (liquibaseBundle != null) {
liquibaseBundles.remove(liquibaseBundle);
}
}

public static class LiquibaseBundle {

private final Bundle bundle;
private final List<String> allowedPackages;

public LiquibaseBundle(Bundle bundle, String allowedPackages) {
if (bundle == null) {
throw new IllegalArgumentException("bundle cannot be empty");
}
if (allowedPackages == null || allowedPackages.isEmpty()) {
throw new IllegalArgumentException("packages cannot be empty");
}
this.bundle = bundle;
this.allowedPackages = Collections.unmodifiableList(Arrays.asList(allowedPackages.split(",")));
}

public Bundle getBundle() {
return bundle;
}

public boolean allowedAllPackages() {
return allowedPackages.size() == 1
&& "*".equals(allowedPackages.get(0));
}

public List<String> getAllowedPackages() {
return allowedPackages;
}

}

public static class OSGIContainerChecker {

private static volatile boolean osgiPlatform = false;

public static boolean isOsgiPlatform() {
return osgiPlatform;
}

static void osgiPlatform() {
osgiPlatform = true;
}
}

}
36 changes: 28 additions & 8 deletions liquibase-core/src/main/java/liquibase/util/LiquibaseUtil.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package liquibase.util;

import liquibase.Scope;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;

import java.io.IOException;
import java.io.InputStream;
Expand Down Expand Up @@ -54,19 +56,37 @@ public static String getBuildNumber() {
// the jar file.
private static String getBuildInfo(String propertyId) {
if (liquibaseBuildProperties == null) {
try {
liquibaseBuildProperties = new Properties();
final Enumeration<URL> propertiesUrls = Scope.getCurrentScope().getClassLoader().getResources("liquibase.build.properties");
while (propertiesUrls.hasMoreElements()) {
final URL url = propertiesUrls.nextElement();
try (InputStream buildProperties = url.openStream()) {
Boolean osgiPlatform = Scope.getCurrentScope().get(Scope.Attr.osgiPlatform, Boolean.class);
if (Boolean.TRUE.equals(osgiPlatform)) {
Bundle bundle = FrameworkUtil.getBundle(LiquibaseUtil.class);
URL propURL = bundle.getEntry("liquibase.build.properties");
if (propURL == null) {
Scope.getCurrentScope().getLog(LiquibaseUtil.class).severe("Cannot read liquibase.build.properties");
} else {
try (InputStream buildProperties = propURL.openStream()) {
liquibaseBuildProperties = new Properties();
if (buildProperties != null) {
liquibaseBuildProperties.load(buildProperties);
}
} catch (IOException e) {
Scope.getCurrentScope().getLog(LiquibaseUtil.class).severe("Cannot read liquibase.build.properties", e);
}
}
} catch (IOException e) {
Scope.getCurrentScope().getLog(LiquibaseUtil.class).severe("Cannot read liquibase.build.properties", e);
} else {
try {
liquibaseBuildProperties = new Properties();
final Enumeration<URL> propertiesUrls = Scope.getCurrentScope().getClassLoader().getResources("liquibase.build.properties");
while (propertiesUrls.hasMoreElements()) {
final URL url = propertiesUrls.nextElement();
try (InputStream buildProperties = url.openStream()) {
if (buildProperties != null) {
liquibaseBuildProperties.load(buildProperties);
}
}
}
} catch (IOException e) {
Scope.getCurrentScope().getLog(LiquibaseUtil.class).severe("Cannot read liquibase.build.properties", e);
}
}
}

Expand Down