Skip to content

Commit

Permalink
Merge pull request #2081 from jherkel/master
Browse files Browse the repository at this point in the history
[2054] fix OSGI support (manifest, class loading)
  • Loading branch information
nvoxland committed Dec 23, 2021
2 parents ab7b2bd + f4dbf0f commit 72e0a24
Show file tree
Hide file tree
Showing 6 changed files with 289 additions and 21 deletions.
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

0 comments on commit 72e0a24

Please sign in to comment.