|
| 1 | +# Adding XA Support for New Databases |
| 2 | + |
| 3 | +This guide explains how to add XA (distributed transaction) support for additional databases to the OJP integration test suite. The current implementation includes full PostgreSQL XA support, which serves as the reference implementation. |
| 4 | + |
| 5 | +## Current Status |
| 6 | + |
| 7 | +**XA Support Implemented:** |
| 8 | +- ✅ PostgreSQL - Full XA support with test coverage |
| 9 | + |
| 10 | +**Infrastructure Ready (Driver Detection Only):** |
| 11 | +- Oracle, SQL Server, DB2, CockroachDB - XADataSource factory methods exist but XA testing is disabled |
| 12 | + |
| 13 | +**Not Supported:** |
| 14 | +- H2, MySQL, MariaDB - Limited XA support in these databases |
| 15 | + |
| 16 | +## Prerequisites |
| 17 | + |
| 18 | +Before adding XA support for a new database, ensure: |
| 19 | + |
| 20 | +1. **Database XA Capability**: The database supports XA transactions with a proper XADataSource implementation |
| 21 | +2. **JDBC Driver**: The JDBC driver with XA support is available (not required at compile-time) |
| 22 | +3. **Privileges**: Database user has required privileges for XA operations |
| 23 | +4. **Test Environment**: Database instance is available for integration testing |
| 24 | + |
| 25 | +## Step-by-Step Guide |
| 26 | + |
| 27 | +### 1. Verify XADataSource Factory Method |
| 28 | + |
| 29 | +Check `ojp-server/src/main/java/org/openjproxy/grpc/server/xa/XADataSourceFactory.java`: |
| 30 | + |
| 31 | +```java |
| 32 | +public static DataSource createXADataSource(String url, String user, String password) throws SQLException { |
| 33 | + // ... |
| 34 | + if (url.contains("_postgresql:") || url.contains("_postgres:")) { |
| 35 | + return createPostgreSQLXADataSource(url, user, password); |
| 36 | + } |
| 37 | + // Add your database check here |
| 38 | + // ... |
| 39 | +} |
| 40 | +``` |
| 41 | + |
| 42 | +If your database doesn't have a factory method yet, add one following the PostgreSQL pattern: |
| 43 | + |
| 44 | +```java |
| 45 | +private static DataSource createYourDatabaseXADataSource(String url, String user, String password) |
| 46 | + throws SQLException { |
| 47 | + // Check if driver is available |
| 48 | + try { |
| 49 | + Class.forName("com.yourdb.jdbc.xa.YourDBXADataSource"); |
| 50 | + } catch (ClassNotFoundException e) { |
| 51 | + throw new SQLException("YourDB XA driver not found. Add yourdb-jdbc jar to classpath."); |
| 52 | + } |
| 53 | + |
| 54 | + // Parse URL to extract connection parameters |
| 55 | + String cleanUrl = url.replaceFirst(".*?_", ""); // Remove ojp prefix |
| 56 | + // Extract host, port, database from cleanUrl |
| 57 | + |
| 58 | + // Create XA DataSource using reflection (no compile-time dependency) |
| 59 | + try { |
| 60 | + Object xaDS = Class.forName("com.yourdb.jdbc.xa.YourDBXADataSource").newInstance(); |
| 61 | + |
| 62 | + // Set connection properties via reflection |
| 63 | + xaDS.getClass().getMethod("setServerName", String.class).invoke(xaDS, host); |
| 64 | + xaDS.getClass().getMethod("setPortNumber", int.class).invoke(xaDS, port); |
| 65 | + xaDS.getClass().getMethod("setDatabaseName", String.class).invoke(xaDS, database); |
| 66 | + xaDS.getClass().getMethod("setUser", String.class).invoke(xaDS, user); |
| 67 | + xaDS.getClass().getMethod("setPassword", String.class).invoke(xaDS, password); |
| 68 | + |
| 69 | + return (DataSource) xaDS; |
| 70 | + } catch (Exception e) { |
| 71 | + throw new SQLException("Failed to create YourDB XA DataSource: " + e.getMessage(), e); |
| 72 | + } |
| 73 | +} |
| 74 | +``` |
| 75 | + |
| 76 | +### 2. Update CSV Test Files |
| 77 | + |
| 78 | +Add XA test entries to your database's CSV file(s): |
| 79 | + |
| 80 | +**Example: `ojp-jdbc-driver/src/test/resources/yourdb_connection.csv`** |
| 81 | + |
| 82 | +```csv |
| 83 | +org.openjproxy.jdbc.Driver,jdbc:ojp[localhost:1059]_yourdb://localhost:5000/testdb,user,pass,false |
| 84 | +org.openjproxy.jdbc.Driver,jdbc:ojp[localhost:1059]_yourdb://localhost:5000/testdb,user,pass,true |
| 85 | +``` |
| 86 | + |
| 87 | +- First line: `isXA=false` - Standard JDBC mode |
| 88 | +- Second line: `isXA=true` - XA mode |
| 89 | + |
| 90 | +Update all relevant CSV files that include your database. |
| 91 | + |
| 92 | +### 3. Ensure Tests Use TestDBUtils Helper |
| 93 | + |
| 94 | +Check that test files are using the `TestDBUtils.createConnection()` helper: |
| 95 | + |
| 96 | +**✅ Correct Pattern:** |
| 97 | +```java |
| 98 | +@ParameterizedTest |
| 99 | +@CsvFileSource(resources = "/yourdb_connection.csv") |
| 100 | +void testOperation(String driver, String url, String user, String pwd, boolean isXA) throws Exception { |
| 101 | + TestDBUtils.ConnectionResult connResult = TestDBUtils.createConnection(url, user, pwd, isXA); |
| 102 | + Connection conn = connResult.getConnection(); |
| 103 | + |
| 104 | + try { |
| 105 | + // Test operations |
| 106 | + conn.createStatement().execute("CREATE TABLE test (id INT)"); |
| 107 | + connResult.commit(); // Handles both XA and standard JDBC |
| 108 | + |
| 109 | + // More operations... |
| 110 | + connResult.startXATransactionIfNeeded(); // For XA only |
| 111 | + conn.createStatement().execute("INSERT INTO test VALUES (1)"); |
| 112 | + connResult.commit(); |
| 113 | + } finally { |
| 114 | + connResult.close(); // Properly closes XA and JDBC connections |
| 115 | + } |
| 116 | +} |
| 117 | +``` |
| 118 | + |
| 119 | +**❌ Incorrect Pattern (ignores isXA parameter):** |
| 120 | +```java |
| 121 | +Connection conn = DriverManager.getConnection(url, user, pwd); // Always JDBC mode |
| 122 | +``` |
| 123 | + |
| 124 | +### 4. Handle Database-Specific XA Requirements |
| 125 | + |
| 126 | +Some databases have specific requirements for XA transactions: |
| 127 | + |
| 128 | +**Privileges:** |
| 129 | +- Oracle requires specific GRANT statements for XA system tables |
| 130 | +- Other databases may have similar requirements |
| 131 | +- Document these in comments or separate setup files |
| 132 | + |
| 133 | +**Configuration:** |
| 134 | +- Some databases require specific connection properties for XA |
| 135 | +- Add these to the XADataSource creation method |
| 136 | + |
| 137 | +**Transaction Isolation:** |
| 138 | +- Verify that the database supports XA transaction isolation levels |
| 139 | +- Test with different isolation levels if needed |
| 140 | + |
| 141 | +### 5. Test XA Functionality |
| 142 | + |
| 143 | +Run tests to verify XA support: |
| 144 | + |
| 145 | +```bash |
| 146 | +# Run all tests for your database (both XA and JDBC modes) |
| 147 | +mvn test -pl ojp-jdbc-driver -Dtest="YourDatabase*Test" |
| 148 | + |
| 149 | +# Check that XA tests actually use XA transactions |
| 150 | +# Look for log messages from XAResource.start(), commit(), end() |
| 151 | +``` |
| 152 | + |
| 153 | +**Validation Checklist:** |
| 154 | +- ✅ Tests run with `isXA=false` (standard JDBC) |
| 155 | +- ✅ Tests run with `isXA=true` (XA mode) |
| 156 | +- ✅ XA transactions are properly started (check server logs for XAResource.start()) |
| 157 | +- ✅ XA transactions are properly committed (check server logs for XAResource.commit()) |
| 158 | +- ✅ No connection leaks (check server logs for HikariCP warnings) |
| 159 | +- ✅ Transactions are properly cleaned up on test failures |
| 160 | +- ✅ No hanging tests |
| 161 | + |
| 162 | +### 6. Document Database-Specific Setup |
| 163 | + |
| 164 | +If your database requires special setup (privileges, configuration), create a setup guide: |
| 165 | + |
| 166 | +```markdown |
| 167 | +# YourDatabase XA Setup Guide |
| 168 | + |
| 169 | +## Required Privileges |
| 170 | +```sql |
| 171 | +GRANT xa_privileges TO test_user; |
| 172 | +``` |
| 173 | + |
| 174 | +## Common Issues |
| 175 | +- Error XYZ: Missing privilege ABC |
| 176 | +- Error 123: Configuration DEF required |
| 177 | +``` |
| 178 | + |
| 179 | +## Reference: PostgreSQL Implementation |
| 180 | + |
| 181 | +PostgreSQL serves as the reference implementation. Key files to review: |
| 182 | + |
| 183 | +### Server-Side |
| 184 | +**`ojp-server/src/main/java/org/openjproxy/grpc/server/xa/XADataSourceFactory.java`:** |
| 185 | +```java |
| 186 | +private static DataSource createPostgreSQLXADataSource(String url, String user, String password) { |
| 187 | + PGXADataSource xaDataSource = new PGXADataSource(); |
| 188 | + // Parse URL and set properties |
| 189 | + xaDataSource.setServerNames(new String[]{host}); |
| 190 | + xaDataSource.setPortNumbers(new int[]{port}); |
| 191 | + xaDataSource.setDatabaseName(database); |
| 192 | + xaDataSource.setUser(user); |
| 193 | + xaDataSource.setPassword(password); |
| 194 | + return xaDataSource; |
| 195 | +} |
| 196 | +``` |
| 197 | + |
| 198 | +### Test-Side |
| 199 | +**`ojp-jdbc-driver/src/test/resources/postgres_connection.csv`:** |
| 200 | +```csv |
| 201 | +org.openjproxy.jdbc.Driver,jdbc:ojp[localhost:1059]_postgresql://localhost:5432/defaultdb,testuser,testpass,false |
| 202 | +org.openjproxy.jdbc.Driver,jdbc:ojp[localhost:1059]_postgresql://localhost:5432/defaultdb,testuser,testpass,true |
| 203 | +``` |
| 204 | + |
| 205 | +**Test Helper Usage:** |
| 206 | +```java |
| 207 | +TestDBUtils.ConnectionResult connResult = TestDBUtils.createConnection(url, user, pwd, isXA); |
| 208 | +Connection conn = connResult.getConnection(); |
| 209 | +// Operations... |
| 210 | +connResult.commit(); // XA-aware commit |
| 211 | +connResult.close(); // Proper cleanup |
| 212 | +``` |
| 213 | + |
| 214 | +## Common Pitfalls |
| 215 | + |
| 216 | +### 1. Not Using TestDBUtils Helper |
| 217 | +**Problem:** Tests create connections with `DriverManager.getConnection()`, ignoring `isXA` parameter. |
| 218 | +**Solution:** Always use `TestDBUtils.createConnection()` which handles XA vs JDBC branching. |
| 219 | + |
| 220 | +### 2. Missing Privileges |
| 221 | +**Problem:** XA operations fail with privilege errors. |
| 222 | +**Solution:** Grant required XA privileges to database user. Document these in your setup guide. |
| 223 | + |
| 224 | +### 3. Connection Leaks |
| 225 | +**Problem:** XA connections not properly closed, causing resource leaks. |
| 226 | +**Solution:** Always call `connResult.close()` in `finally` block. The helper properly ends XA transactions. |
| 227 | + |
| 228 | +### 4. Transaction Not Started |
| 229 | +**Problem:** After `commit()`, next operation fails because no transaction is active. |
| 230 | +**Solution:** Call `connResult.startXATransactionIfNeeded()` after `commit()` if you need to do more work. |
| 231 | + |
| 232 | +### 5. URL Parsing Issues |
| 233 | +**Problem:** XADataSource factory can't parse database URL correctly. |
| 234 | +**Solution:** Test URL parsing thoroughly. Handle different URL formats (with/without options, different separators). |
| 235 | + |
| 236 | +## Testing Checklist |
| 237 | + |
| 238 | +Before marking a database as "XA supported": |
| 239 | + |
| 240 | +- [ ] XADataSource factory method created and tested |
| 241 | +- [ ] CSV files updated with both `isXA=false` and `isXA=true` entries |
| 242 | +- [ ] All tests using the database refactored to use `TestDBUtils.createConnection()` |
| 243 | +- [ ] Tests pass in standard JDBC mode (`isXA=false`) |
| 244 | +- [ ] Tests pass in XA mode (`isXA=true`) |
| 245 | +- [ ] Server logs show XA transactions being used (XAResource.start/commit/end) |
| 246 | +- [ ] No connection leaks in server logs |
| 247 | +- [ ] No hanging tests |
| 248 | +- [ ] Setup requirements documented (if any) |
| 249 | +- [ ] Privilege requirements documented (if any) |
| 250 | +- [ ] Common errors documented with solutions |
| 251 | + |
| 252 | +## Getting Help |
| 253 | + |
| 254 | +- Review PostgreSQL implementation as reference |
| 255 | +- Check `TestDBUtils.java` for XA transaction lifecycle management |
| 256 | +- Review `XADataSourceFactory.java` for DataSource creation patterns |
| 257 | +- Test incrementally - get JDBC mode working first, then add XA |
0 commit comments