diff --git a/hadoop-hdds/common/src/main/resources/ozone-default.xml b/hadoop-hdds/common/src/main/resources/ozone-default.xml
index 5580548bcc..731bf2823b 100644
--- a/hadoop-hdds/common/src/main/resources/ozone-default.xml
+++ b/hadoop-hdds/common/src/main/resources/ozone-default.xml
@@ -2394,4 +2394,99 @@
If enabled, tracing information is sent to tracing server.
+
+ ozone.recon.sql.db.driver
+ org.sqlite.JDBC
+ OZONE, RECON
+
+ Database driver class name available on the
+ Ozone Recon classpath.
+
+
+
+ ozone.recon.sql.db.jdbc.url
+ jdbc:sqlite:/${ozone.recon.db.dir}/ozone_recon_sqlite.db
+ OZONE, RECON
+
+ Ozone Recon SQL database jdbc url.
+
+
+
+ ozone.recon.sql.db.username
+
+ OZONE, RECON
+
+ Ozone Recon SQL database username.
+
+
+
+ ozone.recon.sql.db.password
+
+ OZONE, RECON
+
+ Ozone Recon datbase password.
+
+
+
+ ozone.recon.sql.db.auto.commit
+ false
+ OZONE, RECON
+
+ Sets the Ozone Recon database connection property of auto-commit to
+ true/false.
+
+
+
+ ozone.recon.sql.db.conn.timeout
+ 30000
+ OZONE, RECON
+
+ Sets time in milliseconds before call to getConnection is timed out.
+
+
+
+ ozone.recon.sql.db.conn.max.active
+ 1
+ OZONE, RECON
+
+ The max active connections to the SQL database. The default SQLite
+ database only allows single active connection, set this to a
+ resonable value like 10, for external production database.
+
+
+
+ ozone.recon.sql.db.conn.max.age
+ 1800
+ OZONE, RECON
+
+ Sets maximum time a connection can be active in seconds.
+
+
+
+ ozone.recon.sql.db.conn.idle.max.age
+ 3600
+ OZONE, RECON
+
+ Sets maximum time to live for idle connection in seconds.
+
+
+
+ ozone.recon.sql.db.conn.idle.test.period
+ 60
+ OZONE, RECON
+
+ This sets the time (in seconds), for a connection to remain idle before
+ sending a test query to the DB. This is useful to prevent a DB from
+ timing out connections on its end.
+
+
+
+ ozone.recon.sql.db.conn.idle.test
+ SELECT 1
+ OZONE, RECON
+
+ The query to send to the DB to maintain keep-alives and test for dead
+ connections.
+
+
diff --git a/hadoop-hdds/tools/pom.xml b/hadoop-hdds/tools/pom.xml
index 0e39330889..689bca7a77 100644
--- a/hadoop-hdds/tools/pom.xml
+++ b/hadoop-hdds/tools/pom.xml
@@ -49,9 +49,8 @@ http://maven.apache.org/xsd/maven-4.0.0.xsd">
org.xerialsqlite-jdbc
- 3.8.7
+ 3.25.2
-
diff --git a/hadoop-ozone/ozone-recon-codegen/pom.xml b/hadoop-ozone/ozone-recon-codegen/pom.xml
new file mode 100644
index 0000000000..336fc1ada1
--- /dev/null
+++ b/hadoop-ozone/ozone-recon-codegen/pom.xml
@@ -0,0 +1,58 @@
+
+
+
+ hadoop-ozone
+ org.apache.hadoop
+ 0.5.0-SNAPSHOT
+
+ 4.0.0
+ hadoop-ozone-recon-codegen
+ Apache Hadoop Ozone Recon CodeGen
+
+ 3.11.10
+ 4.1.0
+
+
+
+ org.apache.hadoop
+ hadoop-ozone-common
+
+
+ org.xerial
+ sqlite-jdbc
+ 3.25.2
+
+
+ com.google.inject.extensions
+ guice-multibindings
+ ${guice.version}
+
+
+ org.springframework
+ spring-jdbc
+ 5.1.3.RELEASE
+
+
+ org.jooq
+ jooq-codegen
+ ${jooq.version}
+
+
+ org.jooq
+ jooq-meta
+ ${jooq.version}
+
+
+ org.jooq
+ jooq
+ ${jooq.version}
+
+
+ com.google.inject
+ guice
+ ${guice.version}
+
+
+
\ No newline at end of file
diff --git a/hadoop-ozone/ozone-recon-codegen/src/main/java/org/hadoop/ozone/recon/codegen/JooqCodeGenerator.java b/hadoop-ozone/ozone-recon-codegen/src/main/java/org/hadoop/ozone/recon/codegen/JooqCodeGenerator.java
new file mode 100644
index 0000000000..fce4e0ba5c
--- /dev/null
+++ b/hadoop-ozone/ozone-recon-codegen/src/main/java/org/hadoop/ozone/recon/codegen/JooqCodeGenerator.java
@@ -0,0 +1,170 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.hadoop.ozone.recon.codegen;
+
+import org.hadoop.ozone.recon.schema.ReconSchemaDefinition;
+import org.hadoop.ozone.recon.schema.UtilizationSchemaDefinition;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.multibindings.Multibinder;
+
+/**
+ * Bindings for DDL generation and used by
+ * {@link org.hadoop.ozone.recon.codegen.JooqCodeGenerator}.
+ */
+public class ReconSchemaGenerationModule extends AbstractModule {
+ @Override
+ protected void configure() {
+ // SQL schema creation and related bindings
+ Multibinder schemaBinder =
+ Multibinder.newSetBinder(binder(), ReconSchemaDefinition.class);
+ schemaBinder.addBinding().to(UtilizationSchemaDefinition.class);
+
+ }
+}
diff --git a/hadoop-ozone/ozone-recon-codegen/src/main/java/org/hadoop/ozone/recon/codegen/TableNamingStrategy.java b/hadoop-ozone/ozone-recon-codegen/src/main/java/org/hadoop/ozone/recon/codegen/TableNamingStrategy.java
new file mode 100644
index 0000000000..93c23c4a90
--- /dev/null
+++ b/hadoop-ozone/ozone-recon-codegen/src/main/java/org/hadoop/ozone/recon/codegen/TableNamingStrategy.java
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.hadoop.ozone.recon.codegen;
+
+import org.jooq.codegen.DefaultGeneratorStrategy;
+import org.jooq.meta.Definition;
+import org.jooq.meta.TableDefinition;
+import org.jooq.tools.StringUtils;
+
+/**
+ * Generate Table classes with a different name from POJOS to improve
+ * readability, loaded at runtime.
+ */
+public class TableNamingStrategy extends DefaultGeneratorStrategy {
+ @Override
+ public String getJavaClassName(Definition definition, Mode mode) {
+ if (definition instanceof TableDefinition && mode == Mode.DEFAULT) {
+ StringBuilder result = new StringBuilder();
+
+ result.append(StringUtils.toCamelCase(
+ definition.getOutputName()
+ .replace(' ', '_')
+ .replace('-', '_')
+ .replace('.', '_')
+ ));
+
+ result.append("Table");
+ return result.toString();
+ } else {
+ return super.getJavaClassName(definition, mode);
+ }
+ }
+}
diff --git a/hadoop-ozone/ozone-recon-codegen/src/main/java/org/hadoop/ozone/recon/codegen/package-info.java b/hadoop-ozone/ozone-recon-codegen/src/main/java/org/hadoop/ozone/recon/codegen/package-info.java
new file mode 100644
index 0000000000..2e5cf0f5af
--- /dev/null
+++ b/hadoop-ozone/ozone-recon-codegen/src/main/java/org/hadoop/ozone/recon/codegen/package-info.java
@@ -0,0 +1,22 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Recon code generation support for entities and daos.
+ */
+package org.hadoop.ozone.recon.codegen;
\ No newline at end of file
diff --git a/hadoop-ozone/ozone-recon-codegen/src/main/java/org/hadoop/ozone/recon/schema/ReconSchemaDefinition.java b/hadoop-ozone/ozone-recon-codegen/src/main/java/org/hadoop/ozone/recon/schema/ReconSchemaDefinition.java
new file mode 100644
index 0000000000..72a105e5ff
--- /dev/null
+++ b/hadoop-ozone/ozone-recon-codegen/src/main/java/org/hadoop/ozone/recon/schema/ReconSchemaDefinition.java
@@ -0,0 +1,34 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.hadoop.ozone.recon.schema;
+
+import java.sql.SQLException;
+
+/**
+ * Classes meant to initialize the SQL schema for Recon. The implementations of
+ * this class will be used to create the SQL schema programmatically.
+ * Note: Make sure add a binding for your implementation to the Guice module,
+ * otherwise code-generator will not pick up the schema changes.
+ */
+public interface ReconSchemaDefinition {
+
+ /**
+ * Execute DDL that will create Recon schema.
+ */
+ void initializeSchema() throws SQLException;
+}
diff --git a/hadoop-ozone/ozone-recon-codegen/src/main/java/org/hadoop/ozone/recon/schema/UtilizationSchemaDefinition.java b/hadoop-ozone/ozone-recon-codegen/src/main/java/org/hadoop/ozone/recon/schema/UtilizationSchemaDefinition.java
new file mode 100644
index 0000000000..977a3b3526
--- /dev/null
+++ b/hadoop-ozone/ozone-recon-codegen/src/main/java/org/hadoop/ozone/recon/schema/UtilizationSchemaDefinition.java
@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.hadoop.ozone.recon.schema;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import javax.sql.DataSource;
+
+import org.jooq.impl.DSL;
+import org.jooq.impl.SQLDataType;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.google.inject.Inject;
+
+/**
+ * Programmatic definition of Recon DDL.
+ */
+public class UtilizationSchemaDefinition implements ReconSchemaDefinition {
+
+ private final DataSource dataSource;
+
+ public static final String CLUSTER_GROWTH_DAILY_TABLE_NAME =
+ "cluster_growth_daily";
+
+ @Inject
+ UtilizationSchemaDefinition(DataSource dataSource) {
+ this.dataSource = dataSource;
+ }
+
+ @Override
+ @Transactional
+ public void initializeSchema() throws SQLException {
+ Connection conn = dataSource.getConnection();
+ createClusterGrowthTable(conn);
+ }
+
+ void createClusterGrowthTable(Connection conn) {
+ DSL.using(conn).createTableIfNotExists(CLUSTER_GROWTH_DAILY_TABLE_NAME)
+ .column("timestamp", SQLDataType.TIMESTAMP)
+ .column("datanode_id", SQLDataType.INTEGER)
+ .column("datanode_host", SQLDataType.VARCHAR(1024))
+ .column("rack_id", SQLDataType.VARCHAR(1024))
+ .column("available_size", SQLDataType.BIGINT)
+ .column("used_size", SQLDataType.BIGINT)
+ .column("container_count", SQLDataType.INTEGER)
+ .column("block_count", SQLDataType.INTEGER)
+ .constraint(DSL.constraint("pk_timestamp_datanode_id")
+ .primaryKey("timestamp", "datanode_id"))
+ .execute();
+ }
+
+
+}
diff --git a/hadoop-ozone/ozone-recon-codegen/src/main/java/org/hadoop/ozone/recon/schema/package-info.java b/hadoop-ozone/ozone-recon-codegen/src/main/java/org/hadoop/ozone/recon/schema/package-info.java
new file mode 100644
index 0000000000..3c701f989d
--- /dev/null
+++ b/hadoop-ozone/ozone-recon-codegen/src/main/java/org/hadoop/ozone/recon/schema/package-info.java
@@ -0,0 +1,22 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Common configuration needed to instantiate {@link javax.sql.DataSource}.
+ */
+public interface DataSourceConfiguration {
+ /**
+ * Get database driver class name available on the classpath.
+ */
+ String getDriverClass();
+
+ /**
+ * Get Jdbc Url for the database server.
+ */
+ String getJdbcUrl();
+
+ /**
+ * Get username for the db.
+ */
+ String getUserName();
+
+ /**
+ * Get password for the db.
+ */
+ String getPassword();
+
+ /**
+ * Should autocommit be turned on for the datasource.
+ */
+ boolean setAutoCommit();
+
+ /**
+ * Sets the maximum time (in milliseconds) to wait before a call to
+ * getConnection is timed out.
+ */
+ long getConnectionTimeout();
+
+ /**
+ * Get a string representation of {@link org.jooq.SQLDialect}.
+ */
+ String getSqlDialect();
+
+ /**
+ * In a production database this should be set to something like 10.
+ * SQLite does not allow multiple connections, hence this defaults to 1.
+ */
+ Integer getMaxActiveConnections();
+
+ /**
+ * Sets the maximum connection age (in seconds).
+ */
+ Integer getMaxConnectionAge();
+
+ /**
+ * Sets the maximum idle connection age (in seconds).
+ */
+ Integer getMaxIdleConnectionAge();
+
+ /**
+ * Statement specific to database, usually SELECT 1.
+ */
+ String getConnectionTestStatement();
+
+ /**
+ * How often to test idle connections for being active (in seconds).
+ */
+ Integer getIdleConnectionTestPeriod();
+}
diff --git a/hadoop-ozone/ozone-recon/src/main/java/org/apache/hadoop/ozone/recon/persistence/DefaultDataSourceProvider.java b/hadoop-ozone/ozone-recon/src/main/java/org/apache/hadoop/ozone/recon/persistence/DefaultDataSourceProvider.java
new file mode 100644
index 0000000000..7b28d00488
--- /dev/null
+++ b/hadoop-ozone/ozone-recon/src/main/java/org/apache/hadoop/ozone/recon/persistence/DefaultDataSourceProvider.java
@@ -0,0 +1,74 @@
+package org.apache.hadoop.ozone.recon.persistence;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import javax.sql.DataSource;
+
+import org.apache.commons.lang3.StringUtils;
+import org.sqlite.SQLiteDataSource;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.jolbox.bonecp.BoneCPDataSource;
+
+/**
+ * Provide a {@link javax.sql.DataSource} for the application.
+ */
+public class DefaultDataSourceProvider implements Provider {
+
+ @Inject
+ private DataSourceConfiguration configuration;
+
+ /**
+ * Create a pooled datasource for the application.
+ *
+ * Default sqlite database does not work with a connection pool, actually
+ * most embedded databases do not, hence returning native implementation for
+ * default db.
+ */
+ @Override
+ public DataSource get() {
+ if (StringUtils.contains(configuration.getJdbcUrl(), "sqlite")) {
+ SQLiteDataSource ds = new SQLiteDataSource();
+ ds.setUrl(configuration.getJdbcUrl());
+ return ds;
+ }
+
+ BoneCPDataSource cpDataSource = new BoneCPDataSource();
+
+ cpDataSource.setDriverClass(configuration.getDriverClass());
+ cpDataSource.setJdbcUrl(configuration.getJdbcUrl());
+ cpDataSource.setUsername(configuration.getUserName());
+ cpDataSource.setPassword(configuration.getPassword());
+ cpDataSource.setDefaultAutoCommit(configuration.setAutoCommit());
+ cpDataSource.setConnectionTimeoutInMs(configuration.getConnectionTimeout());
+ cpDataSource.setMaxConnectionsPerPartition(
+ configuration.getMaxActiveConnections());
+ cpDataSource.setMaxConnectionAgeInSeconds(
+ configuration.getMaxConnectionAge());
+ cpDataSource.setIdleMaxAgeInSeconds(
+ configuration.getMaxIdleConnectionAge());
+ cpDataSource.setIdleConnectionTestPeriodInSeconds(
+ configuration.getIdleConnectionTestPeriod());
+ cpDataSource.setConnectionTestStatement(
+ configuration.getConnectionTestStatement());
+
+ return cpDataSource;
+ }
+}
diff --git a/hadoop-ozone/ozone-recon/src/main/java/org/apache/hadoop/ozone/recon/persistence/JooqPersistenceModule.java b/hadoop-ozone/ozone-recon/src/main/java/org/apache/hadoop/ozone/recon/persistence/JooqPersistenceModule.java
new file mode 100644
index 0000000000..c5c83105f1
--- /dev/null
+++ b/hadoop-ozone/ozone-recon/src/main/java/org/apache/hadoop/ozone/recon/persistence/JooqPersistenceModule.java
@@ -0,0 +1,111 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.ozone.recon.persistence;
+
+import static com.google.inject.matcher.Matchers.annotatedWith;
+import static com.google.inject.matcher.Matchers.any;
+
+import java.sql.Connection;
+import javax.sql.DataSource;
+
+import org.jooq.Configuration;
+import org.jooq.ConnectionProvider;
+import org.jooq.SQLDialect;
+import org.jooq.impl.DefaultConfiguration;
+import org.springframework.dao.DataAccessException;
+import org.springframework.jdbc.datasource.DataSourceTransactionManager;
+import org.springframework.jdbc.datasource.DataSourceUtils;
+import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Provider;
+import com.google.inject.Provides;
+import com.google.inject.Singleton;
+
+/**
+ * Persistence module that provides binding for {@link DataSource} and
+ * a MethodInterceptor for nested transactions support.
+ */
+public class JooqPersistenceModule extends AbstractModule {
+
+ private Provider configurationProvider;
+ public static final SQLDialect DEFAULT_DIALECT = SQLDialect.SQLITE;
+
+ public JooqPersistenceModule(
+ Provider configurationProvider) {
+ this.configurationProvider = configurationProvider;
+ }
+
+ @Override
+ protected void configure() {
+ bind(DataSourceConfiguration.class).toProvider(configurationProvider);
+ bind(DataSource.class).toProvider(DefaultDataSourceProvider.class)
+ .in(Singleton.class);
+
+ TransactionalMethodInterceptor interceptor =
+ new TransactionalMethodInterceptor(
+ getProvider(DataSourceTransactionManager.class));
+
+ bindInterceptor(annotatedWith(Transactional.class), any(), interceptor);
+ bindInterceptor(any(), annotatedWith(Transactional.class), interceptor);
+ }
+
+ @Provides
+ @Singleton
+ Configuration getConfiguration(DefaultDataSourceProvider provider) {
+ DataSource dataSource = provider.get();
+
+ return new DefaultConfiguration()
+ .set(dataSource)
+ .set(new SpringConnectionProvider(dataSource))
+ .set(SQLDialect.valueOf(configurationProvider.get().getSqlDialect()));
+ }
+
+ @Provides
+ @Singleton
+ DataSourceTransactionManager provideDataSourceTransactionManager(
+ DataSource dataSource) {
+ return new DataSourceTransactionManager(
+ new TransactionAwareDataSourceProxy(dataSource));
+ }
+
+ /**
+ * This connection provider uses Spring to extract the
+ * {@link TransactionAwareDataSourceProxy} from our BoneCP pooled connection
+ * {@link DataSource}.
+ */
+ static class SpringConnectionProvider implements ConnectionProvider {
+
+ private final DataSource dataSource;
+
+ SpringConnectionProvider(DataSource dataSource) {
+ this.dataSource = dataSource;
+ }
+
+ @Override
+ public Connection acquire() throws DataAccessException {
+ return DataSourceUtils.getConnection(dataSource);
+ }
+
+ @Override
+ public void release(Connection connection) throws DataAccessException {
+ DataSourceUtils.releaseConnection(connection, dataSource);
+ }
+ }
+}
diff --git a/hadoop-ozone/ozone-recon/src/main/java/org/apache/hadoop/ozone/recon/persistence/TransactionalMethodInterceptor.java b/hadoop-ozone/ozone-recon/src/main/java/org/apache/hadoop/ozone/recon/persistence/TransactionalMethodInterceptor.java
new file mode 100644
index 0000000000..4479ddd979
--- /dev/null
+++ b/hadoop-ozone/ozone-recon/src/main/java/org/apache/hadoop/ozone/recon/persistence/TransactionalMethodInterceptor.java
@@ -0,0 +1,76 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.ozone.recon.persistence;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+import org.springframework.jdbc.datasource.DataSourceTransactionManager;
+import org.springframework.transaction.TransactionStatus;
+import org.springframework.transaction.UnexpectedRollbackException;
+import org.springframework.transaction.support.DefaultTransactionDefinition;
+
+import com.google.inject.Provider;
+
+/**
+ * A {@link MethodInterceptor} that implements nested transactions.
+ *
+ * Only the outermost transactional method will commit() or
+ * rollback() the contextual transaction. This can be verified
+ * through {@link TransactionStatus#isNewTransaction()}, which returns
+ * true only for the outermost transactional method call.
+ *
+ */
+public class TransactionalMethodInterceptor implements MethodInterceptor {
+
+ private Provider transactionManagerProvider;
+
+ TransactionalMethodInterceptor(
+ Provider transactionManagerProvider) {
+ this.transactionManagerProvider = transactionManagerProvider;
+ }
+
+ @Override
+ public Object invoke(MethodInvocation invocation) throws Throwable {
+ DataSourceTransactionManager transactionManager =
+ transactionManagerProvider.get();
+
+ DefaultTransactionDefinition transactionDefinition =
+ new DefaultTransactionDefinition();
+ TransactionStatus transaction = transactionManager.getTransaction(
+ transactionDefinition);
+
+ try {
+ Object result = invocation.proceed();
+
+ try {
+ if (transaction.isNewTransaction()) {
+ transactionManager.commit(transaction);
+ }
+ } catch (UnexpectedRollbackException ignore) {
+ }
+
+ return result;
+ } catch (Exception e) {
+ if (transaction.isNewTransaction()) {
+ transactionManager.rollback(transaction);
+ }
+
+ throw e;
+ }
+ }
+}
diff --git a/hadoop-ozone/ozone-recon/src/main/java/org/apache/hadoop/ozone/recon/persistence/package-info.java b/hadoop-ozone/ozone-recon/src/main/java/org/apache/hadoop/ozone/recon/persistence/package-info.java
new file mode 100644
index 0000000000..0ba0fa47ca
--- /dev/null
+++ b/hadoop-ozone/ozone-recon/src/main/java/org/apache/hadoop/ozone/recon/persistence/package-info.java
@@ -0,0 +1,22 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This package defines the persistence interfaces for Recon SQL DB.
+ */
+package org.apache.hadoop.ozone.recon.persistence;
\ No newline at end of file
diff --git a/hadoop-ozone/ozone-recon/src/test/java/org/apache/hadoop/ozone/recon/persistence/AbstractSqlDatabaseTest.java b/hadoop-ozone/ozone-recon/src/test/java/org/apache/hadoop/ozone/recon/persistence/AbstractSqlDatabaseTest.java
new file mode 100644
index 0000000000..2fab93219e
--- /dev/null
+++ b/hadoop-ozone/ozone-recon/src/test/java/org/apache/hadoop/ozone/recon/persistence/AbstractSqlDatabaseTest.java
@@ -0,0 +1,146 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * End to end tests for persistence classes.
+ */
+package org.apache.hadoop.ozone.recon.persistence;
\ No newline at end of file
diff --git a/hadoop-ozone/pom.xml b/hadoop-ozone/pom.xml
index 11498944aa..7010878f62 100644
--- a/hadoop-ozone/pom.xml
+++ b/hadoop-ozone/pom.xml
@@ -49,6 +49,7 @@
s3gatewaydistozone-recon
+ ozone-recon-codegen