diff --git a/pom.xml b/pom.xml
index f91cdff71..9e356e0b4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,8 +12,11 @@
UTF-8
5.7.2
2.22.2
+ 1.2.1
+ 0.0.1
+ 3.18.0
-
+
The Apache Software License, Version 2.0
@@ -149,8 +152,9 @@
org.xerial.sqlite-jdbc;singleton:=true
- *;resolution:=optional
+ *
+ org.sqlite.osgi.SQLiteActivator
@@ -375,5 +379,35 @@
3.12.4
test
+
+
+ org.osgi
+ org.osgi.service.jdbc
+ 1.0.1
+ provided
+
+
+ org.osgi
+ org.osgi.framework
+ 1.10.0
+ provided
+
+
+ org.osgi
+ org.osgi.test.junit5
+ ${osgi-test.version}
+ test
+
+
+ de.laeubisoft
+ osgi-test-framework
+ ${osgi-test-framework.version}
+ test
+
+
+ org.eclipse.platform
+ org.eclipse.osgi
+ ${equinoxVersion}
+
diff --git a/src/main/java/org/sqlite/osgi/SQLiteActivator.java b/src/main/java/org/sqlite/osgi/SQLiteActivator.java
new file mode 100644
index 000000000..8962c35f4
--- /dev/null
+++ b/src/main/java/org/sqlite/osgi/SQLiteActivator.java
@@ -0,0 +1,23 @@
+package org.sqlite.osgi;
+
+import java.util.Hashtable;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.jdbc.DataSourceFactory;
+import org.sqlite.JDBC;
+import org.sqlite.SQLiteJDBCLoader;
+
+public class SQLiteActivator implements BundleActivator {
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+ Hashtable properties = new Hashtable<>();
+ properties.put(DataSourceFactory.OSGI_JDBC_DRIVER_CLASS, JDBC.class.getName());
+ properties.put(DataSourceFactory.OSGI_JDBC_DRIVER_NAME, "SQLite JDBC driver");
+ properties.put(DataSourceFactory.OSGI_JDBC_DRIVER_VERSION, SQLiteJDBCLoader.getVersion());
+ context.registerService(DataSourceFactory.class, new SQLiteDataSourceFactory(), properties);
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {}
+}
diff --git a/src/main/java/org/sqlite/osgi/SQLiteDataSourceFactory.java b/src/main/java/org/sqlite/osgi/SQLiteDataSourceFactory.java
new file mode 100644
index 000000000..63ca0f099
--- /dev/null
+++ b/src/main/java/org/sqlite/osgi/SQLiteDataSourceFactory.java
@@ -0,0 +1,99 @@
+package org.sqlite.osgi;
+
+import java.sql.Driver;
+import java.sql.SQLException;
+import java.util.Properties;
+import java.util.function.Consumer;
+import javax.sql.ConnectionPoolDataSource;
+import javax.sql.DataSource;
+import javax.sql.XADataSource;
+import org.osgi.service.jdbc.DataSourceFactory;
+import org.sqlite.JDBC;
+import org.sqlite.SQLiteConfig;
+import org.sqlite.SQLiteDataSource;
+import org.sqlite.javax.SQLiteConnectionPoolDataSource;
+
+public class SQLiteDataSourceFactory implements DataSourceFactory {
+
+ @Override
+ public DataSource createDataSource(Properties props) throws SQLException {
+ SQLiteDataSource dataSource = new SQLiteDataSource(getConfig(props));
+ setBasicDataSourceProperties(props, dataSource);
+ return dataSource;
+ }
+
+ @Override
+ public ConnectionPoolDataSource createConnectionPoolDataSource(Properties props)
+ throws SQLException {
+
+ SQLiteConnectionPoolDataSource poolDataSource =
+ new SQLiteConnectionPoolDataSource(getConfig(props));
+ setBasicDataSourceProperties(props, poolDataSource);
+ return poolDataSource;
+ }
+
+ @Override
+ public XADataSource createXADataSource(Properties props) throws SQLException {
+ throw new SQLException("XADataSource is not supported by SQLite");
+ }
+
+ @Override
+ public Driver createDriver(Properties props) throws SQLException {
+ return new JDBC();
+ }
+
+ /**
+ * Method to transfer a property to a setter method
+ *
+ * @param props
+ * @param key
+ * @param consumer
+ */
+ private static void setStandardProperty(
+ Properties props, String key, Consumer consumer) {
+ String value = props.getProperty(key);
+ if (value != null) {
+ consumer.accept(value);
+ }
+ }
+
+ /**
+ * Set basic properties common to {@link SQLiteDataSource}s
+ *
+ * @param props
+ * @param dataSource
+ */
+ private static void setBasicDataSourceProperties(
+ Properties props, SQLiteDataSource dataSource) {
+ if (props != null) {
+ setStandardProperty(
+ props, DataSourceFactory.JDBC_DATABASE_NAME, dataSource::setDatabaseName);
+ setStandardProperty(props, DataSourceFactory.JDBC_URL, dataSource::setUrl);
+ }
+ }
+
+ /**
+ * converts user supplied properties into an internal {@link SQLiteConfig} object
+ *
+ * @param userProperties the user properties, might be null
+ * @return a {@link SQLiteConfig} config object reflecting the given user properties
+ */
+ private static SQLiteConfig getConfig(Properties userProperties) {
+ SQLiteConfig config;
+ if (userProperties == null) {
+ config = new SQLiteConfig();
+ } else {
+ Properties properties = new Properties(userProperties);
+ setStandardProperty(
+ userProperties,
+ DataSourceFactory.JDBC_USER,
+ v -> properties.setProperty("user", v));
+ setStandardProperty(
+ userProperties,
+ DataSourceFactory.JDBC_PASSWORD,
+ v -> properties.setProperty("pass", v));
+ config = new SQLiteConfig(properties);
+ }
+ return config;
+ }
+}
diff --git a/src/test/java/org/sqlite/OSGiTest.java b/src/test/java/org/sqlite/OSGiTest.java
new file mode 100644
index 000000000..40be5fd25
--- /dev/null
+++ b/src/test/java/org/sqlite/OSGiTest.java
@@ -0,0 +1,103 @@
+package org.sqlite;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import de.laeubisoft.osgi.junit5.framework.annotations.EmbeddedFramework;
+import de.laeubisoft.osgi.junit5.framework.annotations.WithBundle;
+import de.laeubisoft.osgi.junit5.framework.extension.FrameworkExtension;
+import de.laeubisoft.osgi.junit5.framework.services.FrameworkEvents;
+import java.sql.Connection;
+import java.sql.Driver;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Properties;
+import javax.sql.ConnectionPoolDataSource;
+import javax.sql.DataSource;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.osgi.framework.launch.Framework;
+import org.osgi.service.jdbc.DataSourceFactory;
+import org.osgi.test.common.annotation.InjectService;
+import org.osgi.test.common.service.ServiceAware;
+import org.osgi.test.junit5.service.ServiceExtension;
+import org.sqlite.javax.SQLiteConnectionPoolDataSource;
+
+@WithBundle(value = "org.xerial.sqlite-jdbc", start = true, isolated = true)
+@WithBundle(value = "org.osgi.service.jdbc")
+@ExtendWith(ServiceExtension.class)
+@ExtendWith(FrameworkExtension.class)
+public class OSGiTest {
+
+ @BeforeAll
+ public static void beforeTest(
+ @EmbeddedFramework Framework framework,
+ @InjectService FrameworkEvents frameworkEvents) {
+ FrameworkExtension.printBundles(framework, System.out::println);
+ FrameworkExtension.printComponents(framework, System.out::println);
+ frameworkEvents.assertErrorFree();
+ }
+
+ @InjectService(filter = "(osgi.jdbc.driver.class=org.sqlite.JDBC)")
+ ServiceAware datasourceFactory;
+
+ @BeforeEach
+ public void checkService() {
+ assertEquals(
+ 1,
+ datasourceFactory.size(),
+ "There should be exactly one DataSourceFactory for SQLite!");
+ }
+
+ @Test
+ public void testCreateDriver() throws SQLException {
+ Driver driver = getFactory().createDriver(null);
+ assertClass(JDBC.class, driver);
+ }
+
+ @Test
+ public void testCreateDataSource() throws SQLException {
+ DataSource dataSource = getFactory().createDataSource(null);
+ assertClass(SQLiteDataSource.class, dataSource);
+ }
+
+ @Test
+ public void testCreateConnectionPoolDataSource() throws SQLException {
+ ConnectionPoolDataSource dataSource = getFactory().createConnectionPoolDataSource(null);
+ assertClass(SQLiteConnectionPoolDataSource.class, dataSource);
+ }
+
+ @Test
+ public void testCreateXADataSource() throws SQLException {
+ DataSourceFactory service = getFactory();
+ assertThrows(SQLException.class, () -> service.createXADataSource(null));
+ }
+
+ @Test
+ public void testCreateConnection() throws SQLException {
+
+ Properties props = new Properties();
+ props.setProperty(DataSourceFactory.JDBC_URL, "jdbc:sqlite:");
+ DataSource dataSource = getFactory().createDataSource(props);
+ try (Connection connection = dataSource.getConnection()) {
+ Statement stmt = connection.createStatement();
+ stmt.executeUpdate("create table sample(id, name)");
+ stmt.executeUpdate("insert into sample values(1, \"leo\")");
+ stmt.executeUpdate("insert into sample values(2, \"yui\")");
+ }
+ }
+
+ private DataSourceFactory getFactory() {
+ DataSourceFactory service = datasourceFactory.getService();
+ assertNotNull(service);
+ return service;
+ }
+
+ private static void assertClass(Class> clazz, Object obj) {
+ assertNotNull(obj);
+ assertEquals(clazz.getName(), obj.getClass().getName());
+ }
+}