I'm trying to set up a JNDI database connection for my WAR file which will be deployed on WebLogic 12c. This is what I have:
#Bean
public DataSource dataSource() throws DataSourceLookupFailureException {
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
return dataSourceLookup.getDataSource("jndiName");
}
In the application.properties file:
spring.datasource.jndi-name=jndiName
This is pieced together from two different sections of the Spring Boot guide:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-sql.html
https://docs.spring.io/spring-boot/docs/current/reference/html/howto-data-access.html#howto-configure-a-datasource
I am confused, why is jndiName stated twice? Am I following correctly - is this the correct way to set up?
I am of the impression that Spring Boot will either automatically retrieve the JNDI name from application.properties, so that I do not need to hardcode the JNDI name into getDataSource(), or if I hardcode the JNDI name in then I wouldn't need to include that property in application.properties in the first place.
From my interpretation, the two sections of the guide covering how to set up a JNDI connection appear to contradict each other. What am I getting wrong? What exactly is the correct way to set this up?
Remove your custom bean and use application.properties but in local environment if you want to run embedded container this approach won't work
To use H2 or any other database in embedded container define profile in main method and then configure your data source. This way it works both in local and production
Related
Can we read the property values from the DB instead of setting up it on application.properties ? or even can we update the property value from code if a default value is set up in the application.properties file
You're probably looking for the Spring Cloud Config Server.
The spring context is initialized with the values from application.properties to which your database config belongs as well. You would need to establish the database connection prior to constructing the whole application context and then reading properties from the database. This can probably be achieved by modifiying how Spring works, but I'd rather recommend using a means officially supported by Spring.
I'm new to spring and I'm studying it. And stumbled upon the #Profile annotation.
I want to write a simple project with Spring (not Springboot) to learn how to load properties based on the environment using #profile annotation. Almost everywhere, the examples (Ex1, Ex2) I see only with the Springboot. I'm wondering whether we cannot write a Spring application that can dynamically load the properties based on the environment (dev, prod).
Some examples ( Ex3, Ex4, Ex5) show with the #Profile but those have hardcoded the bean details for each environment like below. Is this how we have to write the property loading?
#Profile("dev")
#Bean
public String devDBCcnnection() {
System.out.println(dbConfiguration.getUrl());
return "DB Connection for Dev";
}
#Profile("test")
#Bean
public String devTestCcnnection() {
System.out.println(dbConfiguration.getDriverClassName());
return "DB Connection for Test";
}
#Profile("prod")
#Bean
public String devProdCcnnection() {
System.out.println("DB Connection for Prod");
return "DB Connection for Prod";
}
It has to write a bean for each profile like in the above example?
Can someone tell me using #Profiles, can't dynamically load the property values like in Spring applications?
Appreciate it if you can give the samples with Spring 5
Almost everywhere, the examples (Ex1, Ex2) I see only with the
Springboot. I'm wondering whether we cannot write a Spring application
that can dynamically load the properties based on the environment
(dev, prod).
Spring boot uses the spring context. The spring context allows you to use profiles. Therefore no problem using profiles with simple Spring project (non spring-boot).
There are many ways that you can use Profiles.
One of them is the example that you gave with specific beans that have #Profile and get registered in spring for a specific profile.
Another one, more commonly used in enteprise applications is to ship a jar application with multiple application.yaml files. So for example you ship your application, containing dev-application.yaml and qa-application.yaml. You can then start your application selecting a specific profile to be active. Then that specific application.yaml will be used when the application starts up to build the spring context. So the aplication will be started with qa-application.yaml and will have a connection to the QA database.
But be careful the default application.yaml will also be loaded. The specific application.yaml for example qa-application.yaml will be loaded on top of default application.yaml.
The following article contains very good information about spring profiles
spring profiles article
Considering my example here, I quotte something relevant from that article.
The Default Profile The default profile is always active. Spring Boot
loads all properties in application.yml into the default profile. We
could rename the configuration file to application-default.yml and it
would work the same.
Other profiles will always be evaluated on top of the default profile.
This means that if a property is defined in the default profile, but
not in the qa profile, the property value will be populated from the
default profile. This is very handy for defining default values that
are valid across all profiles.
In order to activate a specific profile
For non spring-boot projects here is a very good answer spring active profile
For spring-boot projects you can
Use a system variable to start your jar file
java -Dspring.profiles.active=qa -jar myApp.jar
Use an environment property to start your jar file
export SPRING_PROFILES_ACTIVE=qa
java -jar myApp.jar
I am using Spring Boot application and the auto cofiguration is enabled. The main Application file is marked as #EnableAutoConfiguration. The datasource is lookedup from JNDI is configured using java config and the class which create the datasource is marked as #Configuration.
I have a test class as below.
#RunWith( SpringJUnit4ClassRunner.class )
#WebAppConfiguration
#ContextConfiguration( classes = Application.class )
public class TestSomeBusiness {}
The issue is when I run the test case, the datasource jndi lookup happens, which fails because the test case is not running inside a server environment. As far as I know the classes in classpath marked with #Configuration are executed and that the reason the datasource lookup is being called.
The work around for now I have found is instead of JNDI lookup create the datasource using DriverManagerDataSource, so that even if its not a server environment the datasource lookup won't fail.
My questions are:
1) How do we generally deal with datasource (when looking up from JNDI) in
spring boot application for testing ?
2) Is there a way to exclude the datasource configuration class from being called when executing test case ?
3) Should I create an embedded server so that the JNDI lookup can be done when executing test case ?
2) Is there a way to exclude the datasource configuration class from being called when executing test case ?
You can add a application.properties config file into your src/test/resources and spring boot would pick those configurations in test environments. I suppose, you have application.properties in your src/main/resources like this:
spring.datasource.jndi-name=some_jndi
This JNDI resource will be used in your production environment. For your test environment you can use a, say MySQL database, by adding these configurations into your test application.properties:
spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=dbuser
spring.datasource.password=dbpass
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
3) Should I create an embedded server so that the JNDI lookup can be
done when executing test case ?
As i said, you can totally bypass the fact that you're using JNDI for production by adding test specific configurations.
1) How do we generally deal with datasource (when looking up from
JNDI) in spring boot application for testing ?
You can mock JNDI resources using facilities available in org.springframework.mock.jndi package. For example by using SimpleNamingContextBuilder you can:
SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();
builder.bind("jndi_name", dataSource);
builder.activate();
The other option is, of course, using Non JNDI resources in test environments.
My WAR application is deployed to Amazon Elastic Beanstalk. They don't support JNDI, but I need it for JPA and unit tests. What JNDI context factory I can use as a workaround? I need a simple open source solution, that would allow me to configure entire factory through jndi.properties. I tried GuiceyFruit, but looks like it doesn't allow data source configuration inside one file. Any suggestions?
ps. OpenEJB will work, but it's an overkill for such a simple task
Try Simple-JNDI. It gives you an in-memory implementation of a JNDI Service and allows you to populate the JNDI environment with objects defined in property files. There is also support for loading datasources or connection pools configured in a file.
To get a connection pool you have to create a file like this:
type=javax.sql.DataSource
driver=com.sybase.jdbc3.jdbc.SybDriver
pool=myDataSource
url=jdbc:sybase:Tds:servername:5000
user=user
password=password
In your application you can access the pool via
Context ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup("path/to/your/connectionPool");
You can find more about it at https://github.com/h-thurow/Simple-JNDI.
I have a .properties file in my application which contains dataSource properties.
I set up a JNDI reference to this dataSource using the following code :
// first I create MyDataSource from the properties found in the .properties file
//then :
Context initContext = new InitialContext();
initContext.createSubcontext("jdbc");
initContext.createSubcontext("jdbc/oracle");
initContext.rebind(jdbc/oracle/myDataSource, MyDataSource);
If I use a lookup in this application, the dataSource is found :
Context initContext = new InitialContext();
BasicDataSource dataSource =
(BasicDataSource) initContext.lookup("jdbc/oracle/myDataSource")
//everything works fine and I can use my dataSource to getConnection,
//requests, etc...
Now I would like to use this dataSource in another application. But if I do the same lookup than previously, I don't find myDataSource (whereas there is still the previous application in tomcat and the jndi binding is done on start-up with the help of a listener).
How can I get myDataSource in this second application, given that I can't use a Tomcat's resource in server.xml or a context.xml file (for different reasons I have to use this .properties file)?
Thanks
"local" JDNI directories are read-only in Tomcat. Nevertheless, you can bind "global" JNDI resources in a LifecycleListener, and then "link" them to your context(s)(*):
You need to implement org.apache.catalina.LifecycleListener http://tomcat.apache.org/tomcat-6.0-doc/api/org/apache/catalina/LifecycleListener.html
Then register it in your server.xml like this (along with the other listeners):
<Listener className="yourlistener.YourLifecycleListener"/>
Your listener should await for 2 events:
public void lifecycleEvent(final LifecycleEvent event) {
if (Lifecycle.START_EVENT.equals(event.getType())) {
// Create your datasource instance...
Context initContext = new InitialContext();
initContext.createSubcontext("jdbc");
initContext.createSubcontext("jdbc/oracle");
initContext.rebind("jdbc/oracle/myDataSource", myDataSource);
} else if (Lifecycle.STOP_EVENT.equals(event.getType())) {
// unbind...
}
}
Then you'll have to propagate resource accesses by "linking" them from "global" JNDI directory to "local" JNDI directory using ResourceLink element in your META-INF/context.xml:
<ResourceLink name="jdbc/oracle/myDataSource" global="jdbc/oracle/myDataSource"
type="javax.sql.DataSource" />
That worked for me so far.
(*) Some notes:
There's an advantage on using lifecycle listeners. Since the order of context creation is not guaranteed. The advantage is that all of your contexts will see this object created.
If you need to create and configure datasource creation more dynamically that on lifecycle listener creation, note that you can bind a custom class implementing the Factory pattern.
To avoid classloading incompatibility problems, consider putting your listener, datasource, etc. classes in a jar file in the Tomcat lib directory, so they're included y the common classloader.
Regards.
What you are trying to do is not going to work. J2EE applications are not allowed to modify the JNDI environment provided by the application server (J2EE spec, section 5.2.2) and the Tomcat JNDI documentation also states, that each web applications gets each own read-only JNDI environment. I'm not sure why binding/rebinding your datasource is not failing immediately and why it's working within the same web application, but even such application-internal usage of the JNDI environment is undocumented behaviour, which I would not rely on.
A couple people have already commented on this, but I think the answer to your question is: Tomcat has a file called server.xml that you need to use. A good reference I have used before is below:
http://tomcat.apache.org/tomcat-5.5-doc/jndi-resources-howto.html
Resources defined here will be visible to all apps deployed (if set up correctly). If you set up a JNDI resource in your apps context or web xml file, it should only be available to your app.
JNDI context are private to each webapp. Context created in one app can't be accessed by others.
Try to create an entry in GlobalNamingResources and links in both webapps using <ResourceLink> to see if it works.
I used this setup before to read from both apps but never tried to write from one. So not sure if it will work.
Actually, it is possible to access others JNDI resources, if the servlet implements org.apache.catalina.ContainerServlet. This interface has a org.apache.catalina.Wrapper attribute, that is "populated" by the container itself.
through that, I created a simple application to monitor resources.
BUT, I would like to do that in a listener, so my resource monitor could start when the container starts. Anyone knows a way?