I was using Spring-jdbc with org.apache.commons.dbcp.BasicDataSource using the username and password for the connection. I want to use BasicDataSource because I only have one connection.
I had this code:
<bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
id="dataSource">
<property name="driverClassName" value="${database.driverClassName}" />
<property name="url" value="${database.url}" />
<property name="username" value="${database.username}" />
<property name="password" value="${database.password}" />
</bean>
Now I have to use authentication based in Oracle Wallet, I don't have problem in a simple application test without Spring but I can't integrate this new authentication with Spring. Does anyone know How I can do it??
You mention "simple application test" so I'm assuming you need to configure your unit tests. In a unit test config class (for example class TestSpringWebConfig extends SpringWebConfig) this gets you an Oracle datasource using a wallet (bonus: the following uses a proxy database account):
System.setProperty("oracle.net.tns_admin", "path/to/your/tnsnames");
OracleDataSource ds = new OracleDataSource();
Properties props = new Properties();
props.put("oracle.net.wallet_location", "(source=(method=file)(method_data=(directory=path/to/your/wallet)))");
/*
Use the following only if you have a proxy user database account instead of a normal DB account
A test user's username could go here though
*/
props.put(OracleConnection.CONNECTION_PROPERTY_PROXY_CLIENT_NAME, "proxy-user-name");
ds.setConnectionProperties( props );
ds.setURL("jdbc:oracle:thin:/#dbAlias"); //dbAlias should match what's in your tnsnames
return ds;
This also assumes you have the following in your JDK:
In JAVA_HOME/jre/lib/security/java.security, add the following to the "List of providers":
security.provider.11=oracle.security.pki.OraclePKIProvider
And add the following jars from Oracle to JAVA_HOME/jre/lib/ext:
osdt_cert.jar
osdt_core.jar
oraclepki.jar
And of course, all of the above assumes the ojdbc7 jar is in your application's classpath already.
Related
I am implementing a multitenant application with the spring security saml extention.
I have a Service Provider (SP) for each tenant.
All SPs runs on the same server exposed with SP-specific 2nd level domain:
sp1.myapp.com/myapi/1/
sp1.myapp.com/myapi/2/
In each SP metadata file I have configured the tenant-specific AssertionConsumerService.
When I test the SSO Login, I get a KO on SP side when it gets the response of the Identity Provider (IDP).
On Log side i see only:
ERROR [BaseSAMLMessageDecoder] SAML message intended destination endpoint 'https://sp1.myapp.com/myapi/saml/SSO' did not match the recipient endpoint 'https://default.myapp.com/myapi/saml/SSO'
Where the 'https://default.myapp.com/myapi/saml/SSO' is the URL set as serverName of the load balancer context provider:
<bean id="lbContextProvider" class="org.springframework.security.saml.context.SAMLContextProviderLB" init-method="afterPropertiesSet">
<property name="metadata" ref="metadata" />
<property name="keyManager" ref="keyManager" />
<property name="scheme" value="https" />
<property name="serverName" value="default.myapp.com" />
<property name="contextPath" value="/myapi" />
<property name="serverPort" value="443" />
<property name="includeServerPortInRequestURL" value="true" />
</bean>
Question
In the docs.spring.io/spring-security-saml I see that
Service provider can now define multiple assertion consumer endpoints with same binding
How can I configure it?
Does it conflict with load balancer context provider?
Can I provide multiple AssertionConsumerService with different 2nd level domains without reproduction this conflict?
I already tested:
This question seems to be fixed with the LB, but anyone knows if I can provide multiple serverName to load balancer context provider (maybe with a dynamic pick)?
Disable the checking of the InResponseToField as suggested at ch.13 docs.spring.io/spring-security-saml and for this and this question.
Configure the defaultTargetUrl of the successRedirectHandler (where I am using a custom superclass of org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler) as suggested for this question. In addition this solution is not multitenant.
<bean id="successRedirectHandler" class="org.MySamlAuthenticationSuccessHandler"
init-method="afterPropertiesSet">
<property name="contextPath" value="/myapi" />
<property name="defaultTargetUrl" value="https://default.myapp.com/myapi/saml/SSO"/>
<property name="requireProxyWrapping" value="false"/>
</bean>
Customize SAMLContextProviderLB by extending SamlContextProviderLB.
In custom class, add constructor and initialize with default values.
Override getLocalAndPeerEntity/getLocalEntity/populateLocalEntityId. In each of this method set lbDomain based on domain in requestURL.
above approach worked for me.
When I debug my Mule application i have error:
org.springframework.dao.DataAccessResourceFailureException: Error retrieving database metadata; nested exception is org.springframework.jdbc.support.MetaDataAccessException: Could not get Connection for extracting meta data; nested exception is org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is java.sql.SQLException: No suitable driver found for jdbc:h2:~/test
my java code:
SimpleJdbcCall call = new SimpleJdbcCall(jdbcTemplate.getDataSource()).withProcedureName("my_procedure_name").withSchemaName("my_schema");
...
call.execute(in)
my aaplicationContext:
<bean id="dataSource2"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${db2.driver}" />
<property name="url" value="${db2.url}" />
<property name="username" value="${db2.user}" />
<property name="password" value="${db2.password}" />
</bean>
my app_name.properties:
db2.url=jdbc:h2:~/test
db2.driver=org.h2.Driver
db2.user=sa
db2.password=
my pom:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.200</version>
</dependency>
my classpath:
<classpathentry kind="var" path="M2_REPO/com/h2database/h2/1.4.200/h2-1.4.200.jar"/>
log info after run application:
INFO org.springframework.jdbc.datasource.DriverManagerDataSource - Loaded JDBC driver: org.h2.Driver
Also i put h2-1.4.200.jar (when I starting my application from AnypointStudio) to:
c:\...\plugins\org.mule.tooling.server.3.9.0_6.4.0.201908221250\mule\lib\user\
and:
c:\...\plugins\org.mule.tooling.server.3.9.0_6.4.0.201908221250\mule\lib\boot\
and:
c:\...\plugins\org.mule.tooling.server.3.9.0_6.4.0.201908221250\mule\lib\mule\
and when I run application from mule server h2-1.4.200.jar, I put here:
..\mule-standalone-3.9.0\lib\boot
and
..\mule-standalone-3.9.0\lib\user
and:
..\mule-standalone-3.9.0\lib\mule
why application driver h2 not found ? what's the problem ?
Thanks.
You are mixing 3 different ways of managing the JDBC driver dependency, and also duplicating the library in the runtimes. That also makes it more difficult to understand the problem, and to deploy an application.
Let's start with Maven. It looks like you are using the right dependency in the pom, as long as it is in the section.
If the project is using Maven, there should not be any need to look into , unless it is out of sync with Maven. You should not change the class path manually, or edit the build path in Anypoint Studio. These are part of Studio/Eclipse .classpath files and should be left alone. Be sure to update the project so Studio regenerates the classpath.
About MULE_HOME\lib\boot, MULE_HOME\lib\user, MULE_HOME\lib\mule (either in Studio or standalone), you should not put the library in there. The Maven dependency is enough and you are duplicating the versions. Even if you can share a library in lib\user, it is not recommended. It makes more difficult to replicate sanely a deployment and again, there is no need for the JDBC driver. You should not put anything at all in lib\boot nor lib\mule nor the other subdirectories of lib. These are reserved for the runtime.
Try removing all those extras first and see what happens with a clean deployment.
Update:
Once the libraries are cleaned up, take into account that the Spring class may have some classloading issues itself. It is almost always better to use a datasource pool implementation. There are several to choose, like c3p0, dbcp, and others.
One example with c3p0:
<spring:bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<spring:property name="driverClass" value="${JDBC.driver}"/>
<spring:property name="jdbcUrl" value="${JDBC.URL}"/>
<spring:property name="user" value="${JDBC.user}"/>
<spring:property name="password" value="${JDBC.password}"/>
<spring:property name="minPoolSize" value="5"/>
<spring:property name="maxPoolSize" value="20"/>
</spring:bean>
See https://help.mulesoft.com/s/article/Spring-based-datasources for more examples.
I have spring application configured via annotations. Here is part of my config
#Configuration
#EnableTransactionManagement
public class JpaSpringConfiguration {
#Bean(destroyMethod = "close")
#Lazy
#Primary
public BasicDataSource dataSource(#Value("${statistics.hostname}") String statisticsHostname) {
final BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("org.postgresql.Driver");
String url = String.format("jdbc:postgresql://%s:5432/statistics-db", statisticsHostname);
dataSource.setUrl(url);
....
return dataSource;
}
#Bean
public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
final PropertyPlaceholderConfigurer placeholderConfigurer = new PropertyPlaceholderConfigurer();
placeholderConfigurer.setSystemPropertiesMode(SYSTEM_PROPERTIES_MODE_OVERRIDE);
Properties properties = new Properties();
properties.setProperty("statistics.hostname", "localhost");
placeholderConfigurer.setProperties(properties);
return placeholderConfigurer;
}
Until recently we had xml configuration
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="properties">
<props>
<prop key="statistics.hostname">localhost</prop>
</props>
</property>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" lazy-init="true" destroy-method="close">
<property name="driverClassName" value="org.postgresql.Driver" />
<property name="url" value="jdbc:postgresql://${statistics.hostname}:5432/statistics-db" />
<property name="username" value="user" />
<property name="password" value="password" />
</bean>
When user selected different server to connect to we set system property and closed application context and refreshed
System.setProperty("statistics.hostname", hostname)
applicationContext.close()
applicationContext.refresh()
This does not work when I use annotation configuration.
My questions are:
why it does not work now?
how to get rid of setting hostname via system property altogether?
EDIT: I just found out that I forgot ${} around the name of the parameter in method dataSource(). So it works now but question 2 still remains.
not sure why it doesnt work, but you may try to do couple more things:
Is closing context before refresh really needed? Try to only refresh it.
You can mark your bean as #RefreshScope ( but it requires spring cloud ) and refresh it using /refresh endpoint. That would require another endpoint to actually update your host on a bean before calling refresh.
"how to get rid of setting hostname via system property altogether?"
pass that to property file which is the way it is normally configured. If you are using spring boot, then you only have to configure:
spring.datasource.url=
spring.datasource.username=
spring.datasource.password=
...
properties. Datasource bean would be created using those values for you.
I am training to integrate the Struts2 and Spring and Hibernate.I using a properties file to set the dataSource:
<context:property-placeholder location="classpath:db.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
And this is the db.properties following:
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc\:mysql\://localhost\:3306/sp3
jdbc.username=root
jdbc.password=123456
I find the example about this in spring-reference,but I just don't know why I must use ${jdbc.XXXXX} but not ${XXXXX}.I try to write "username=root","password=123456",and then it cause "Access denied for user 'Administrator'#'localhost' (using password: YES)"
If using the Expression Language principle:${jdbc.username} meanings "getJdbc().getUsername();"because in the struts-tag,${model} means getModel(),is it right?
I find the source about PropertyPlaceholderConfigurer and ComboPooledDataSource ,but I can not find any code about getJdbc();
Thank you for your help.
I don't understand what you're asking. jdbc.username is just a text, or a key if you want. You could have used "BLABLABLA=root" in db.properties and in your xml <property name="user" value="${BLABLABLA}" />.
Probably you are confusing PropertyPlaceholderConfigurer with PropertyOverrideConfigurer.
If you had the problems with your properties, you would have got an exception something like->
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'XYZ' in string value "${XYZ}".
What do you mean by 'everything is ok'? Were you able to access DB with previous setup?
The exception you are getting seems related to username and password combination OR you don;t have proper rights/GRANTS.
jdbc.XXXXX is just a key in the property file. You could have used anything in place of it.When you perform ${something} it would just pick the value for key 'something' from the property file and use it for populating the properties of bean.
For example :
<property name="jdbcUrl" value="${jdbc.url}" /> is just setting the value for a field named 'jdbcUrl' in class ComboPooledDataSource.
I've been doing a research on connection pool with JDBC api and classes. But still I don't know how to configure a Connection Pool class for a java web project. As you may know, the Connection Pool is a Singleton Class that encapsulates the JDBC apis. But the Connection Class is started once when the web project is deployed on Tomcat Server, I wonder if there's something needed to be done with Web.xml configuration file, to let Tomcat Server load the Connection Pool Class.
Thank you very much for your time!
There a many ways of doing this. JNDI is one way - but it is not so "user friendly" in tomcat. The datasource also can be configured in Spring. For e.g.:
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
The most simplest way of doing this is to create a ServletContextListener class, create your datasource and put it into ServletContext so that the same instance could be retrieved from ServletContext in your project.
See my answer here how to create ServletContextListener.
See also:
How to use JNDI DataSource provided by Tomcat in Spring?