Skip to content

Support for the Data Service Specification #738

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 36 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>5.7.2</junit.version>
<surefire.version>2.22.2</surefire.version>
<osgi-test.version>1.2.1</osgi-test.version>
<osgi-test-framework.version>0.0.1</osgi-test-framework.version>
<equinoxVersion>3.18.0</equinoxVersion>
</properties>

<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
Expand Down Expand Up @@ -149,8 +152,9 @@
<instructions>
<Bundle-SymbolicName>org.xerial.sqlite-jdbc;singleton:=true</Bundle-SymbolicName>
<Import-Package>
*;resolution:=optional
*
</Import-Package>
<Bundle-Activator>org.sqlite.osgi.SQLiteActivator</Bundle-Activator>
</instructions>
</configuration>
</plugin>
Expand Down Expand Up @@ -375,5 +379,35 @@
<version>3.12.4</version>
<scope>test</scope>
</dependency>
<!-- dependencies related to OSGi support -->
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.service.jdbc</artifactId>
<version>1.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.framework</artifactId>
<version>1.10.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.test.junit5</artifactId>
<version>${osgi-test.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.laeubisoft</groupId>
<artifactId>osgi-test-framework</artifactId>
<version>${osgi-test-framework.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.platform</groupId>
<artifactId>org.eclipse.osgi</artifactId>
<version>${equinoxVersion}</version>
</dependency>
</dependencies>
</project>
23 changes: 23 additions & 0 deletions src/main/java/org/sqlite/osgi/SQLiteActivator.java
Original file line number Diff line number Diff line change
@@ -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<String, Object> 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 {}
}
99 changes: 99 additions & 0 deletions src/main/java/org/sqlite/osgi/SQLiteDataSourceFactory.java
Original file line number Diff line number Diff line change
@@ -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<String> 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 <code>null</code>
* @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;
}
}
103 changes: 103 additions & 0 deletions src/test/java/org/sqlite/OSGiTest.java
Original file line number Diff line number Diff line change
@@ -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> 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());
}
}