Skip to content

Hikari to UCP Recipe #183

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

Merged
merged 13 commits into from
May 5, 2025
Merged
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
42 changes: 42 additions & 0 deletions recipes/hikariucp/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
*.log

### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
.idea
.idea/**

### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache

### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/

### VS Code ###
.vscode/
/.jpb/**

### Mac OS ###
.DS_Store
124 changes: 124 additions & 0 deletions recipes/hikariucp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# Hikari to UCP Open Rewrite Recipe

Current Version is `0.0.1-SNAPSHOT` and is under development. Please file GitHub issues for issues, enhancements and more.

## Hikari to UCP rewrite Recipe

> **_NOTE:_** In the pre release the rewrite works best with `properties` files. `YAML` files works too but the formatting of the outcome isn't pretty.

This recipe will change the Hikare connection pool parameters and dependency from Hikari to Oracle Universal Connection Pooling (UCP). The [UCP documentation](https://docs.oracle.com/en/database/oracle/oracle-database/23/jjucp/index.html).

The following properties are rewritten:

### Maven dependencies `pom.xml`

The Hikari dependency is removed and replaced with SPring Boot starters for Oracle UCP and Oracle Wallet (commonly used to connect to an autonomous database (ADB)).

```xml
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
```

is changed to:

```xml
<dependency>
<groupId>com.oracle.database.spring</groupId>
<artifactId>oracle-spring-boot-starter-ucp</artifactId>
<version>25.1.0</version>
</dependency>
```

The following dependency is removed (all ojdbc* variants) and replaced by the UCP Starter:

```xml
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc*</artifactId>
</dependency>
```

And the following dependency is added, as it is commonly used to connect to an autonomous database (ADB):

```xml
<dependency>
<groupId>com.oracle.database.spring</groupId>
<artifactId>oracle-spring-boot-starter-wallet</artifactId>
<version>25.1.0</version>
</dependency>
```

### Spring Boot Connection Pooling configuration `application.properties`

> **_NOTE:_** The recipe will change Hikari's milliseconds to seconds.

The following properties are rewritten. [UCP documentation](https://docs.oracle.com/en/database/oracle/oracle-database/23/jjucp/index.html)

| Hikari Property | Oracle UCP Property | Notes |
|-----------------|---------------------|-------|
| N/A | `spring.datasource.driver-class-name` | Will be set to `oracle.jdbc.OracleDriver` |
| N/A | `spring.datasource.type` | Will be set to `oracle.ucp.jdbc.PoolDataSource` |
| `spring.datasource.hikari.pool-name` | `spring.datasource.oracleucp.connection-pool-name` | |
| `spring.datasource.hikari.maximum-pool-size` | `spring.datasource.oracleucp.max-pool-size` | |
| `spring.datasource.hikari.minimum-idle` | `spring.datasource.oracleucp.min-pool-size` | |
| `spring.datasource.hikari.connection-timeout` | `spring.datasource.oracleucp.connection-wait-timeout` | |
| `spring.datasource.hikari.idle-timeout` | `spring.datasource.oracleucp.inactive-connection-timeout` | |
| `spring.datasource.hikari.connection-test-query` | `spring.datasource.oracleucp.s-q-l-for-validate-connection` | |
| `spring.datasource.hikari.max-lifetime` | `spring.datasource.oracleucp.max-connection-reuse-time` | |
| `spring.datasource.hikari.validation-timeout` | `spring.datasource.oracleucp.connection-validation-timeout` | |

The following UCP properties are added:

| Oracle UCP Property | Value | Notes |
|---------------------|-------|-------|
| `spring.datasource.oracleucp.initial-pool-size` | 5 | [UCP documentation](https://docs.oracle.com/en/database/oracle/oracle-database/23/jjucp/index.html) |

The following Hikari Properties do not have identical UCP properties and will be commented out in the rewritten properties file. [UCP documentation](https://docs.oracle.com/en/database/oracle/oracle-database/23/jjucp/index.html)

| Hikari UCP Property | Notes |
|---------------------|-------|
| `spring.datasource.hikari.auto-commit` | Use Oracle JDBC driver connection property autoCommit |
| `spring.datasource.hikari.register-mbeans` | UCP always attempts registration |
| `spring.datasource.hikari.thread-factory` | UCP supports setTaskManager instead |
| `spring.datasource.hikari.scheduled-executor` | UCP supports setTaskManager instead |
| `spring.datasource.hikari.keepalive-time` | Closest is to use driver connection properties oracle.net.keepAlive + oracle.net.TCP_KEEPIDLE |

## Build the Open Rewrite Recipe

The repo is using maven. To build the recipe run the following command:

```shell
mvn install
```

## To use the Recipe

In the repository you want to test your recipe against, update the `pom.xml` to include the following:

```xml
<project>
<build>
<plugins>
<plugin>
<groupId>org.openrewrite.maven</groupId>
<artifactId>rewrite-maven-plugin</artifactId>
<version>6.3.2</version> <!-- Verify the version, March 2025 -->
<configuration>
<activeRecipes>
<recipe>ConvertHikariToUCP </recipe>
</activeRecipes>
</configuration>
<dependencies>
<dependency>
<groupId>com.oracle.cloud.recipes</groupId>
<artifactId>hikariucp</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
```
134 changes: 134 additions & 0 deletions recipes/hikariucp/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright (c) 2025, Oracle and/or its affiliates. -->
<!-- Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. -->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.oracle.cloud.recipes</groupId>
<artifactId>hikariucp</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>hikari-ucp</name>
<description>Openrewrite Recipe to convert Hikari Connection Pool to Oracle UCP</description>

<url>https://github.com/oracle/spring-cloud-oracle/tree/hikari-ucp-recipe/recipes/hikari-ucp</url>

<organization>
<name>Oracle America, Inc.</name>
<url>https://www.oracle.com</url>
</organization>
<licenses>
<license>
<name>The Universal Permissive License (UPL), Version 1.0</name>
<url>https://oss.oracle.com/licenses/upl/</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<name>Oracle</name>
<email>obaas_ww at oracle.com</email>
<organization>Oracle America, Inc.</organization>
<organizationUrl>https://www.oracle.com</organizationUrl>
</developer>
</developers>
<scm>
<url>https://github.com/oracle/spring-cloud-oracle</url>
<connection>scm:git:https://github.com/oracle/spring-cloud-oracle.git</connection>
<developerConnection>scm:git:[email protected]:oracle/spring-cloud-oracle.git</developerConnection>
</scm>

<properties>
<java.version>17</java.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<rewrite.version>8.48.1</rewrite.version> <!-- Latest as of Mar 2025 -->
<rewrite.recipe.version>3.3.0</rewrite.recipe.version> <!-- Latest as of Mar 2025 -->
<maven.rewrite.plugin.version>6.3.2</maven.rewrite.plugin.version> <!-- Latest as of Mar 2025 -->
<junit.version>5.12.1</junit.version> <!-- Latest as of Mar 2025 -->
</properties>

<dependencies>
<dependency>
<groupId>org.openrewrite</groupId>
<artifactId>rewrite-java</artifactId>
<version>${rewrite.version}</version>
</dependency>

<dependency>
<groupId>org.openrewrite</groupId>
<artifactId>rewrite-properties</artifactId>
<version>${rewrite.version}</version>
</dependency>

<dependency>
<groupId>org.openrewrite</groupId>
<artifactId>rewrite-yaml</artifactId>
<version>${rewrite.version}</version>
</dependency>

<dependency>
<groupId>org.openrewrite</groupId>
<artifactId>rewrite-maven</artifactId>
<version>${rewrite.version}</version>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.openrewrite</groupId>
<artifactId>rewrite-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.openrewrite.recipe</groupId>
<artifactId>rewrite-recipe-bom</artifactId>
<version>${rewrite.recipe.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>${junit.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<plugins>
<plugin>
<groupId>org.openrewrite.maven</groupId>
<artifactId>rewrite-maven-plugin</artifactId>
<version>${maven.rewrite.plugin.version}</version>
<!-- <configuration>-->
<!-- <exportDatatables>true</exportDatatables>-->
<!-- <activeRecipes>-->
<!-- <recipe>ConvertHikariToUCP</recipe>-->
<!-- </activeRecipes>-->
<!-- </configuration>-->
<dependencies>
<dependency>
<groupId>org.openrewrite.recipe</groupId>
<artifactId>rewrite-spring</artifactId>
<version>5.24.1</version>
</dependency>
</dependencies>
</plugin>

</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (c) 2025, Oracle and/or its affiliates.
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/

package com.oracle.cloud.recipes.hikariucp;

import org.openrewrite.NlsRewrite;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.properties.PropertiesVisitor;
import org.openrewrite.properties.tree.Properties;

public class ConvertMsToSecondsInPropertiesRecipe extends Recipe {

private final String keyRegex;

// Takes a keyRegex parameter to specify which property keys to target (e.g., Hikari timeout properties).
public ConvertMsToSecondsInPropertiesRecipe(String keyRegex) {
this.keyRegex = keyRegex;
}

// Extends PropertiesVisitor to process each Properties.Entry (key-value pair) in application.properties.
@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {

return new PropertiesVisitor<ExecutionContext>() {

@Override
public Properties visitEntry(Properties.Entry entry, ExecutionContext ctx) {
if (entry.getKey().matches(keyRegex)) {
String value = entry.getValue().getText().trim();
try {
long ms = Long.parseLong(value);
double seconds = ms / 1000.0;
String newValue = String.valueOf(seconds);
Properties.Value updatedValue = entry.getValue().withText(newValue);
return entry.withValue(updatedValue);
} catch (NumberFormatException e) {
// If the value isn't a valid number, ignore it.
}
}
return super.visitEntry(entry, ctx);
}

};
}

@Override
public @NlsRewrite.DisplayName String getDisplayName() {
return "Convert milliseconds to seconds for Hikari properties";
}

@Override
public @NlsRewrite.Description String getDescription() {
return "Transforms millisecond values to seconds for Hikari connection pool properties matching the given regex.";
}
}
Loading
Loading