Skip to content

Commit 71078d7

Browse files
author
Christoph Läubrich
committed
Support for the Data Service Specification
Add a first basic implementation of the DataSourceFactory
1 parent c7b54a2 commit 71078d7

File tree

4 files changed

+261
-2
lines changed

4 files changed

+261
-2
lines changed

pom.xml

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@
1212
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
1313
<junit.version>5.7.2</junit.version>
1414
<surefire.version>2.22.2</surefire.version>
15+
<osgi-test.version>1.2.1</osgi-test.version>
16+
<osgi-test-framework.version>0.0.1</osgi-test-framework.version>
17+
<equinoxVersion>3.18.0</equinoxVersion>
1518
</properties>
16-
19+
1720
<licenses>
1821
<license>
1922
<name>The Apache Software License, Version 2.0</name>
@@ -149,8 +152,9 @@
149152
<instructions>
150153
<Bundle-SymbolicName>org.xerial.sqlite-jdbc;singleton:=true</Bundle-SymbolicName>
151154
<Import-Package>
152-
*;resolution:=optional
155+
*
153156
</Import-Package>
157+
<Bundle-Activator>org.sqlite.osgi.SQLiteActivator</Bundle-Activator>
154158
</instructions>
155159
</configuration>
156160
</plugin>
@@ -375,5 +379,35 @@
375379
<version>3.12.4</version>
376380
<scope>test</scope>
377381
</dependency>
382+
<!-- dependencies related to OSGi support -->
383+
<dependency>
384+
<groupId>org.osgi</groupId>
385+
<artifactId>org.osgi.service.jdbc</artifactId>
386+
<version>1.0.1</version>
387+
<scope>provided</scope>
388+
</dependency>
389+
<dependency>
390+
<groupId>org.osgi</groupId>
391+
<artifactId>org.osgi.framework</artifactId>
392+
<version>1.10.0</version>
393+
<scope>provided</scope>
394+
</dependency>
395+
<dependency>
396+
<groupId>org.osgi</groupId>
397+
<artifactId>org.osgi.test.junit5</artifactId>
398+
<version>${osgi-test.version}</version>
399+
<scope>test</scope>
400+
</dependency>
401+
<dependency>
402+
<groupId>de.laeubisoft</groupId>
403+
<artifactId>osgi-test-framework</artifactId>
404+
<version>${osgi-test-framework.version}</version>
405+
<scope>test</scope>
406+
</dependency>
407+
<dependency>
408+
<groupId>org.eclipse.platform</groupId>
409+
<artifactId>org.eclipse.osgi</artifactId>
410+
<version>${equinoxVersion}</version>
411+
</dependency>
378412
</dependencies>
379413
</project>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package org.sqlite.osgi;
2+
3+
import java.util.Hashtable;
4+
import org.osgi.framework.BundleActivator;
5+
import org.osgi.framework.BundleContext;
6+
import org.osgi.service.jdbc.DataSourceFactory;
7+
import org.sqlite.JDBC;
8+
import org.sqlite.SQLiteJDBCLoader;
9+
10+
public class SQLiteActivator implements BundleActivator {
11+
12+
@Override
13+
public void start(BundleContext context) throws Exception {
14+
Hashtable<String, Object> properties = new Hashtable<>();
15+
properties.put(DataSourceFactory.OSGI_JDBC_DRIVER_CLASS, JDBC.class.getName());
16+
properties.put(DataSourceFactory.OSGI_JDBC_DRIVER_NAME, "SQLite JDBC driver");
17+
properties.put(DataSourceFactory.OSGI_JDBC_DRIVER_VERSION, SQLiteJDBCLoader.getVersion());
18+
context.registerService(DataSourceFactory.class, new SQLiteDataSourceFactory(), properties);
19+
}
20+
21+
@Override
22+
public void stop(BundleContext context) throws Exception {}
23+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package org.sqlite.osgi;
2+
3+
import java.sql.Driver;
4+
import java.sql.SQLException;
5+
import java.util.Properties;
6+
import java.util.function.Consumer;
7+
import javax.sql.ConnectionPoolDataSource;
8+
import javax.sql.DataSource;
9+
import javax.sql.XADataSource;
10+
import org.osgi.service.jdbc.DataSourceFactory;
11+
import org.sqlite.JDBC;
12+
import org.sqlite.SQLiteConfig;
13+
import org.sqlite.SQLiteDataSource;
14+
import org.sqlite.javax.SQLiteConnectionPoolDataSource;
15+
16+
public class SQLiteDataSourceFactory implements DataSourceFactory {
17+
18+
@Override
19+
public DataSource createDataSource(Properties props) throws SQLException {
20+
SQLiteDataSource dataSource = new SQLiteDataSource(getConfig(props));
21+
setBasicDataSourceProperties(props, dataSource);
22+
return dataSource;
23+
}
24+
25+
@Override
26+
public ConnectionPoolDataSource createConnectionPoolDataSource(Properties props)
27+
throws SQLException {
28+
29+
SQLiteConnectionPoolDataSource poolDataSource =
30+
new SQLiteConnectionPoolDataSource(getConfig(props));
31+
setBasicDataSourceProperties(props, poolDataSource);
32+
return poolDataSource;
33+
}
34+
35+
@Override
36+
public XADataSource createXADataSource(Properties props) throws SQLException {
37+
throw new SQLException("XADataSource is not supported by SQLite");
38+
}
39+
40+
@Override
41+
public Driver createDriver(Properties props) throws SQLException {
42+
return new JDBC();
43+
}
44+
45+
/**
46+
* Method to transfer a property to a setter method
47+
*
48+
* @param props
49+
* @param key
50+
* @param consumer
51+
*/
52+
private static void setStandardProperty(
53+
Properties props, String key, Consumer<String> consumer) {
54+
String value = props.getProperty(key);
55+
if (value != null) {
56+
consumer.accept(value);
57+
}
58+
}
59+
60+
/**
61+
* Set basic properties common to {@link SQLiteDataSource}s
62+
*
63+
* @param props
64+
* @param dataSource
65+
*/
66+
private static void setBasicDataSourceProperties(
67+
Properties props, SQLiteDataSource dataSource) {
68+
if (props != null) {
69+
setStandardProperty(
70+
props, DataSourceFactory.JDBC_DATABASE_NAME, dataSource::setDatabaseName);
71+
setStandardProperty(props, DataSourceFactory.JDBC_URL, dataSource::setUrl);
72+
}
73+
}
74+
75+
/**
76+
* converts user supplied properties into an internal {@link SQLiteConfig} object
77+
*
78+
* @param userProperties the user properties, might be <code>null</code>
79+
* @return a {@link SQLiteConfig} config object reflecting the given user properties
80+
*/
81+
private static SQLiteConfig getConfig(Properties userProperties) {
82+
SQLiteConfig config;
83+
if (userProperties == null) {
84+
config = new SQLiteConfig();
85+
} else {
86+
Properties properties = new Properties(userProperties);
87+
setStandardProperty(
88+
userProperties,
89+
DataSourceFactory.JDBC_USER,
90+
v -> properties.setProperty("user", v));
91+
setStandardProperty(
92+
userProperties,
93+
DataSourceFactory.JDBC_PASSWORD,
94+
v -> properties.setProperty("pass", v));
95+
config = new SQLiteConfig(properties);
96+
}
97+
return config;
98+
}
99+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package org.sqlite;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertNotNull;
5+
import static org.junit.jupiter.api.Assertions.assertThrows;
6+
7+
import de.laeubisoft.osgi.junit5.framework.annotations.EmbeddedFramework;
8+
import de.laeubisoft.osgi.junit5.framework.annotations.WithBundle;
9+
import de.laeubisoft.osgi.junit5.framework.extension.FrameworkExtension;
10+
import de.laeubisoft.osgi.junit5.framework.services.FrameworkEvents;
11+
import java.sql.Connection;
12+
import java.sql.Driver;
13+
import java.sql.SQLException;
14+
import java.sql.Statement;
15+
import java.util.Properties;
16+
import javax.sql.ConnectionPoolDataSource;
17+
import javax.sql.DataSource;
18+
import org.junit.jupiter.api.BeforeAll;
19+
import org.junit.jupiter.api.BeforeEach;
20+
import org.junit.jupiter.api.Test;
21+
import org.junit.jupiter.api.extension.ExtendWith;
22+
import org.osgi.framework.launch.Framework;
23+
import org.osgi.service.jdbc.DataSourceFactory;
24+
import org.osgi.test.common.annotation.InjectService;
25+
import org.osgi.test.common.service.ServiceAware;
26+
import org.osgi.test.junit5.service.ServiceExtension;
27+
import org.sqlite.javax.SQLiteConnectionPoolDataSource;
28+
29+
@WithBundle(value = "org.xerial.sqlite-jdbc", start = true, isolated = true)
30+
@WithBundle(value = "org.osgi.service.jdbc")
31+
@ExtendWith(ServiceExtension.class)
32+
@ExtendWith(FrameworkExtension.class)
33+
public class OSGiTest {
34+
35+
@BeforeAll
36+
public static void beforeTest(
37+
@EmbeddedFramework Framework framework,
38+
@InjectService FrameworkEvents frameworkEvents) {
39+
FrameworkExtension.printBundles(framework, System.out::println);
40+
FrameworkExtension.printComponents(framework, System.out::println);
41+
frameworkEvents.assertErrorFree();
42+
}
43+
44+
@InjectService(filter = "(osgi.jdbc.driver.class=org.sqlite.JDBC)")
45+
ServiceAware<DataSourceFactory> datasourceFactory;
46+
47+
@BeforeEach
48+
public void checkService() {
49+
assertEquals(
50+
1,
51+
datasourceFactory.size(),
52+
"There should be exactly one DataSourceFactory for SQLite!");
53+
}
54+
55+
@Test
56+
public void testCreateDriver() throws SQLException {
57+
Driver driver = getFactory().createDriver(null);
58+
assertClass(JDBC.class, driver);
59+
}
60+
61+
@Test
62+
public void testCreateDataSource() throws SQLException {
63+
DataSource dataSource = getFactory().createDataSource(null);
64+
assertClass(SQLiteDataSource.class, dataSource);
65+
}
66+
67+
@Test
68+
public void testCreateConnectionPoolDataSource() throws SQLException {
69+
ConnectionPoolDataSource dataSource = getFactory().createConnectionPoolDataSource(null);
70+
assertClass(SQLiteConnectionPoolDataSource.class, dataSource);
71+
}
72+
73+
@Test
74+
public void testCreateXADataSource() throws SQLException {
75+
DataSourceFactory service = getFactory();
76+
assertThrows(SQLException.class, () -> service.createXADataSource(null));
77+
}
78+
79+
@Test
80+
public void testCreateConnection() throws SQLException {
81+
82+
Properties props = new Properties();
83+
props.setProperty(DataSourceFactory.JDBC_URL, "jdbc:sqlite:");
84+
DataSource dataSource = getFactory().createDataSource(props);
85+
try (Connection connection = dataSource.getConnection()) {
86+
Statement stmt = connection.createStatement();
87+
stmt.executeUpdate("create table sample(id, name)");
88+
stmt.executeUpdate("insert into sample values(1, \"leo\")");
89+
stmt.executeUpdate("insert into sample values(2, \"yui\")");
90+
}
91+
}
92+
93+
private DataSourceFactory getFactory() {
94+
DataSourceFactory service = datasourceFactory.getService();
95+
assertNotNull(service);
96+
return service;
97+
}
98+
99+
private static void assertClass(Class<?> clazz, Object obj) {
100+
assertNotNull(obj);
101+
assertEquals(clazz.getName(), obj.getClass().getName());
102+
}
103+
}

0 commit comments

Comments
 (0)