Skip to content

Commit 2f0adf3

Browse files
authored
21 configuration providers with caches should be able to recognized by the driver (#26)
* fix issue#21 * add tests for issue#21 * use getSecretCredentialClient instead of DefaultAzureCredential & add try block to Connection * remove the @disable in AzureAppConfigurationProviderURLParserTest, which is set by mistake in history
1 parent 25891e6 commit 2f0adf3

File tree

4 files changed

+245
-3
lines changed

4 files changed

+245
-3
lines changed

ojdbc-provider-azure/src/main/java/oracle/jdbc/provider/azure/configuration/AzureAppConfigurationProvider.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import com.azure.security.keyvault.secrets.SecretClient;
4949
import com.azure.security.keyvault.secrets.SecretClientBuilder;
5050
import oracle.jdbc.OracleConnection;
51+
import oracle.jdbc.spi.OracleConfigurationCachableProvider;
5152
import oracle.jdbc.util.OracleConfigurationCache;
5253
import oracle.jdbc.util.OracleConfigurationProviderNetworkError;
5354
import oracle.jdbc.spi.OracleConfigurationProvider;
@@ -67,7 +68,7 @@
6768
* </p>
6869
*/
6970
public class AzureAppConfigurationProvider
70-
implements OracleConfigurationProvider {
71+
implements OracleConfigurationCachableProvider {
7172
private static final OracleJsonFactory JSON_FACTORY = new OracleJsonFactory();
7273

7374
private static final String CONNECT_DESCRIPTOR_PROPERTIES_NAME =
@@ -270,6 +271,12 @@ private Properties refreshProperties(String location)
270271
throw new OracleConfigurationProviderNetworkError(e);
271272
}
272273
}
274+
275+
@Override
276+
public Properties removeProperties(String location) {
277+
Properties deletedProp = cache.remove(location);
278+
return deletedProp;
279+
}
273280
}
274281

275282

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package oracle.jdbc.provider.azure.configuration;
2+
3+
import com.azure.data.appconfiguration.ConfigurationClient;
4+
import com.azure.data.appconfiguration.ConfigurationClientBuilder;
5+
import com.azure.data.appconfiguration.models.ConfigurationSetting;
6+
import com.azure.identity.ClientSecretCredentialBuilder;
7+
import oracle.jdbc.datasource.impl.OracleDataSource;
8+
import oracle.jdbc.provider.TestProperties;
9+
import oracle.jdbc.provider.azure.AzureTestProperty;
10+
import oracle.jdbc.provider.azure.authentication.AzureAuthenticationMethod;
11+
import org.junit.jupiter.api.Assertions;
12+
import org.junit.jupiter.api.Test;
13+
14+
import java.sql.Connection;
15+
import java.sql.ResultSet;
16+
import java.sql.SQLException;
17+
import java.sql.Statement;
18+
19+
import static org.junit.jupiter.api.Assertions.*;
20+
21+
class AzureAppConfigurationProviderTest {
22+
23+
/**
24+
* Verify that the cache is purged after hitting 1017 error.
25+
* Specifically, get connection to the same url twice, but modify the 'user'
26+
* every time.
27+
* The provided app configuration should have correct username, password and
28+
* correct connect descriptor to connect to Database.
29+
**/
30+
@Test
31+
public void testCachePurged() throws SQLException {
32+
ConfigurationClient client = getSecretCredentialClient();
33+
String APP_CONFIG_NAME=
34+
TestProperties.getOrAbort(AzureTestProperty.AZURE_APP_CONFIG_NAME);
35+
String APP_CONFIG_KEY =
36+
TestProperties.getOrAbort(AzureTestProperty.AZURE_APP_CONFIG_KEY);
37+
String APP_CONFIG_LABEL =
38+
TestProperties.getOrAbort(AzureTestProperty.AZURE_APP_CONFIG_LABEL);
39+
String username = "user";
40+
String originalUrl =
41+
"jdbc:oracle:thin:@config-azure://" + APP_CONFIG_NAME +
42+
"?key=" + APP_CONFIG_KEY + "&label=" + APP_CONFIG_LABEL;
43+
44+
String url = composeUrlWithServicePrincipleAuthentication(originalUrl);
45+
46+
// Retrieve original value of 'user'
47+
String originalKeyValue =
48+
client.getConfigurationSetting(APP_CONFIG_KEY + username,
49+
APP_CONFIG_LABEL).getValue();
50+
51+
// Set value of 'user' wrong
52+
client.setConfigurationSetting( APP_CONFIG_KEY + username,
53+
APP_CONFIG_LABEL, originalKeyValue + "wrong");
54+
55+
// Connection fails: hit 1017
56+
SQLException exception = assertThrows(SQLException.class,
57+
() -> tryConnection(url), "Should throw an SQLException");
58+
Assertions.assertEquals(exception.getErrorCode(), 1017);
59+
60+
// Set value of 'user' correct
61+
ConfigurationSetting result =
62+
client.setConfigurationSetting(APP_CONFIG_KEY + username,
63+
APP_CONFIG_LABEL, originalKeyValue);
64+
Assertions.assertEquals(originalKeyValue, result.getValue());
65+
66+
// Connection succeeds
67+
try (Connection conn = tryConnection(url)) {
68+
Assertions.assertNotNull(conn);
69+
70+
Statement st = conn.createStatement();
71+
ResultSet rs = st.executeQuery("SELECT 'Hello, db' FROM sys.dual");
72+
Assertions.assertNotNull(rs.next());
73+
Assertions.assertEquals("Hello, db", rs.getString(1));
74+
}
75+
}
76+
77+
/**
78+
* Helper function: try to get connection form specified url
79+
**/
80+
private Connection tryConnection(String url) throws SQLException {
81+
OracleDataSource ds = new OracleDataSource();
82+
ds.setURL(url);
83+
Connection conn = ds.getConnection();
84+
return conn;
85+
}
86+
87+
/**
88+
* Similar to the method in AzureAppConfigurationProviderURLParserTest
89+
**/
90+
private static ConfigurationClient getSecretCredentialClient() {
91+
return new ConfigurationClientBuilder()
92+
.credential( new ClientSecretCredentialBuilder()
93+
.clientId(TestProperties.getOrAbort(AzureTestProperty.AZURE_CLIENT_ID))
94+
.clientSecret(
95+
TestProperties.getOrAbort(AzureTestProperty.AZURE_CLIENT_SECRET))
96+
.tenantId(TestProperties.getOrAbort(AzureTestProperty.AZURE_TENANT_ID))
97+
.build())
98+
.endpoint("https://" + TestProperties.getOrAbort(
99+
AzureTestProperty.AZURE_APP_CONFIG_NAME) + ".azconfig.io")
100+
.buildClient();
101+
}
102+
103+
/**
104+
* Use {@link AzureAuthenticationMethod#SERVICE_PRINCIPLE} as its
105+
* authentication method.
106+
**/
107+
private String composeUrlWithServicePrincipleAuthentication(String originalUrl){
108+
String[] options = new String[] {
109+
"AUTHENTICATION=AZURE_SERVICE_PRINCIPAL",
110+
"AZURE_CLIENT_ID=" + TestProperties.getOrAbort(
111+
AzureTestProperty.AZURE_CLIENT_ID),
112+
"AZURE_CLIENT_SECRET=" + TestProperties.getOrAbort(
113+
AzureTestProperty.AZURE_CLIENT_SECRET),
114+
"AZURE_TENANT_ID=" + TestProperties.getOrAbort(
115+
AzureTestProperty.AZURE_TENANT_ID)};
116+
return String.format("%s&%s", originalUrl, String.join("&", options));
117+
}
118+
}

ojdbc-provider-oci/src/main/java/oracle/jdbc/provider/oci/configuration/OciDatabaseToolsConnectionProvider.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import oracle.jdbc.provider.oci.vault.Secret;
1919
import oracle.jdbc.provider.oci.vault.SecretFactory;
2020
import oracle.jdbc.provider.parameter.ParameterSet;
21+
import oracle.jdbc.spi.OracleConfigurationCachableProvider;
2122
import oracle.jdbc.spi.OracleConfigurationProvider;
2223
import oracle.jdbc.util.OracleConfigurationCache;
2324
import oracle.jdbc.util.OracleConfigurationProviderNetworkError;
@@ -34,7 +35,7 @@
3435
* </p>
3536
*/
3637
public class OciDatabaseToolsConnectionProvider
37-
implements OracleConfigurationProvider {
38+
implements OracleConfigurationCachableProvider {
3839

3940
private static final String CONFIG_TIME_TO_LIVE =
4041
"config_time_to_live";
@@ -304,6 +305,12 @@ private Secret requestSecretFromOCI(String secretId) {
304305
.getContent();
305306
}
306307

308+
@Override
309+
public Properties removeProperties(String location) {
310+
Properties deletedProp = cache.remove(location);
311+
return deletedProp;
312+
}
313+
307314
private Properties refreshProperties(String location)
308315
throws OracleConfigurationProviderNetworkError {
309316
try {
@@ -312,4 +319,5 @@ private Properties refreshProperties(String location)
312319
throw new OracleConfigurationProviderNetworkError(bmcException);
313320
}
314321
}
315-
}
322+
}
323+

ojdbc-provider-oci/src/test/java/oracle/jdbc/provider/oci/configuration/OciDatabaseToolsConnectionProviderTest.java

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111
import com.oracle.bmc.databasetools.requests.CreateDatabaseToolsConnectionRequest;
1212
import com.oracle.bmc.databasetools.requests.DeleteDatabaseToolsConnectionRequest;
1313
import com.oracle.bmc.databasetools.requests.GetDatabaseToolsConnectionRequest;
14+
import com.oracle.bmc.databasetools.requests.UpdateDatabaseToolsConnectionRequest;
1415
import com.oracle.bmc.databasetools.responses.CreateDatabaseToolsConnectionResponse;
1516
import com.oracle.bmc.databasetools.responses.DeleteDatabaseToolsConnectionResponse;
1617
import com.oracle.bmc.databasetools.responses.GetDatabaseToolsConnectionResponse;
18+
import com.oracle.bmc.databasetools.responses.UpdateDatabaseToolsConnectionResponse;
1719
import com.oracle.bmc.http.internal.BaseSyncClient;
1820
import oracle.jdbc.datasource.impl.OracleDataSource;
1921
import oracle.jdbc.provider.TestProperties;
@@ -33,6 +35,8 @@
3335
import java.util.Arrays;
3436
import java.util.Objects;
3537

38+
import static org.junit.jupiter.api.Assertions.assertThrows;
39+
3640
/**
3741
* Verifies the {@link OciDatabaseToolsConnectionProvider} as implementing
3842
* behavior specified by its JavaDoc.
@@ -177,6 +181,80 @@ public void testGetPropertiesFromDeletedConnection() {
177181
() -> PROVIDER.getConnectionProperties(ocid));
178182
}
179183

184+
/**
185+
* Verify that the cache is purged after hitting 1017 error.
186+
* Specifically, get connection to the same url twice, but modify the 'user'
187+
* every time.
188+
* A new db tools connection is created and deleted for this test.
189+
**/
190+
@Test
191+
public void testCachePurged() throws SQLException {
192+
String OCI_DISPLAY_NAME = "display_name_for_connection";
193+
String OCI_USERNAME = "admin";
194+
String OCI_PASSWORD_OCID = TestProperties.getOrAbort(
195+
OciTestProperty.OCI_PASSWORD_OCID);
196+
String OCI_DATABASE_OCID = TestProperties.getOrAbort(
197+
OciTestProperty.OCI_DATABASE_OCID);
198+
String OCI_COMPARTMENT_ID = TestProperties.getOrAbort(
199+
OciTestProperty.OCI_COMPARTMENT_ID);
200+
String OCI_DATABASE_CONNECTION_STRING = getConnectionStringFromAutonomousDatabase(
201+
OCI_DATABASE_OCID);
202+
203+
// Create new Connection
204+
CreateDatabaseToolsConnectionResponse createResponse = sendCreateConnRequest(
205+
OCI_USERNAME, OCI_PASSWORD_OCID, OCI_DISPLAY_NAME, OCI_COMPARTMENT_ID,
206+
OCI_DATABASE_CONNECTION_STRING, OCI_DATABASE_OCID);
207+
208+
// The db tools connection is being created.
209+
Assertions.assertEquals(201,
210+
createResponse.get__httpStatusCode__());
211+
212+
// Retrieve OCID of Connection
213+
String ocid = createResponse
214+
.getDatabaseToolsConnection().getId();
215+
216+
// The url used to connect to Database
217+
String url = "jdbc:oracle:thin:@config-ocidbtools://" + ocid;
218+
219+
// Set value of 'user' wrong
220+
UpdateDatabaseToolsConnectionDetails updateDatabaseToolsConnectionDetails =
221+
UpdateDatabaseToolsConnectionOracleDatabaseDetails.builder()
222+
.userName(OCI_USERNAME + "wrong")
223+
.build();
224+
225+
UpdateDatabaseToolsConnectionResponse updateResponse =
226+
sendUpdateConnRequest(ocid, updateDatabaseToolsConnectionDetails);
227+
Assertions.assertEquals(202, updateResponse.get__httpStatusCode__());
228+
229+
// Connection fails: hit 1017
230+
SQLException exception = assertThrows(SQLException.class,
231+
() -> tryConnection(url), "Should throw an SQLException");
232+
Assertions.assertEquals(exception.getErrorCode(), 1017);
233+
234+
// Set value of 'user' correct
235+
UpdateDatabaseToolsConnectionDetails updateDatabaseToolsConnectionDetails2 = UpdateDatabaseToolsConnectionOracleDatabaseDetails.builder()
236+
.userName(OCI_USERNAME).build();
237+
238+
UpdateDatabaseToolsConnectionResponse updateResponse2 =
239+
sendUpdateConnRequest(ocid, updateDatabaseToolsConnectionDetails2);
240+
Assertions.assertEquals(202, updateResponse2.get__httpStatusCode__());
241+
242+
// Connection succeeds
243+
Connection conn = tryConnection(url);
244+
Assertions.assertNotNull(conn);
245+
246+
Statement st = conn.createStatement();
247+
ResultSet rs = st.executeQuery("SELECT 'Hello, db' FROM sys.dual");
248+
Assertions.assertNotNull(rs.next());
249+
Assertions.assertEquals("Hello, db", rs.getString(1));
250+
251+
// Finally delete Connection
252+
DeleteDatabaseToolsConnectionResponse deleteResponse = sendDeleteConnRequest(
253+
ocid);
254+
Assertions.assertEquals(202,
255+
deleteResponse.get__httpStatusCode__()); /* Request accepted. This db tools connection will be deleted */
256+
}
257+
180258
/**
181259
* Helper function: send create DB Tools Connection request
182260
* @param OCI_DATABASE_OCID The OCID of the Autonomous Database
@@ -268,6 +346,27 @@ private GetDatabaseToolsConnectionResponse sendGetConnRequest(String ocid) {
268346
return response;
269347
}
270348

349+
/**
350+
* Helper function: send update DB Tools Connection request
351+
* @param ocid The OCID of DB Tools Connection
352+
* @param updateDatabaseToolsConnectionDetails
353+
* The updateDatabaseToolsConnectionDetails to update to this
354+
* connection
355+
* @return UpdateDatabaseToolsConnectionResponse
356+
*/
357+
private UpdateDatabaseToolsConnectionResponse sendUpdateConnRequest(String ocid, UpdateDatabaseToolsConnectionDetails updateDatabaseToolsConnectionDetails){
358+
/* Create a request and dependent object(s). */
359+
UpdateDatabaseToolsConnectionRequest updateDatabaseToolsConnectionRequest
360+
= UpdateDatabaseToolsConnectionRequest.builder()
361+
.databaseToolsConnectionId(ocid)
362+
.updateDatabaseToolsConnectionDetails(updateDatabaseToolsConnectionDetails)
363+
.build();
364+
365+
/* Send request to the Client */
366+
UpdateDatabaseToolsConnectionResponse response = client.updateDatabaseToolsConnection(updateDatabaseToolsConnectionRequest);
367+
return response;
368+
}
369+
271370
/**
272371
* Helper function: send get Autonomous Database request
273372
* @param OCI_DATABASE_OCID The OCID of the Autonomous Database
@@ -300,4 +399,14 @@ private String getConnectionStringFromAutonomousDatabase(
300399
.getValue();
301400
return CONNECTION_STRING;
302401
}
402+
403+
/**
404+
* Helper function: try to get connection form specified url
405+
**/
406+
private Connection tryConnection(String url) throws SQLException {
407+
OracleDataSource ds = new OracleDataSource();
408+
ds.setURL(url);
409+
Connection conn = ds.getConnection();
410+
return conn;
411+
}
303412
}

0 commit comments

Comments
 (0)