Using java annotation with dynamic parameter - java

What I'm trying to achieve is have a #Resource with a dynamic name parameter. Specifically, I want to inject a DataSource object using #Resource(name = "{JNDI_NAME_PARAM}") because we can have many datasources configured in an application server, and the datasource used by the application is defined in an .xml or .config file. Since I do not know the name of the datasource during compile time I need to be able to get it at runtime. Right now I'm injecting a custom #ApplicationScoped bean which creates a datasource in its #PostConstruct method using InitialContext().lookup(). However I'm curious (mostly because it is more elegant) as to how I could achieve injection using the #Resource annotation.
I COULD create a custom default JNDI name in the app server and change the datasource it points to when needed but this can't work with more than one deployment and many times we have the application deployed twice, once in a test database and once in a production database so having the JNDI point at two different datasources at the same time.

You can use the Method based injection.
It requires the setter method (setMyDB).
public class Test {
public javax.sql.DataSource myDB;
#Resource(name="student")
private void setMyDB(javax.sql.DataSource ds) {
myDB = ds;
}
}
If the names are known, we can have multiple resources under
#Resources({
#Resource(your type)
#Resource(your type)
})

Related

Spring: Using PropertyPlaceholderConfigurer support outside of application context

My applications requirements mean that I need to create application beans manually at runtime and then add them to the application context.
These beans belong to third party libraries so I cannot modify them e.g. a TibjmsConnectionFactory
So my factory class that creates these beans needs to be provided with a Properties object that will set username, password, connectionTimeouts etc
Ideally I'd like to use Spring's property support so I do not need to convert Strings to Integers etc
Also, the Properties provided to my factory class will not be the same Properties used for by the PropertyPlaceholderConfigurer in my overall ApplicationContext
How do I achieve this, or is it even possible?
public class MyCustomFactoryStrategy {
#Override
public TibjmsConnectionFactory create(Properties properties) {
TibjmsConnectionFactory connectionFactory = new TibjmsConnectionFactory();
connectionFactory.setServerUrl(properties.getProperty("emsServerUrl")); // this is a string
connectionFactory.setConnAttemptCount(new Integer(properties.getProperty("connAttemptCount"))); // this is an integer
...
return connectionFactory;
}
Have a look in this post. I think it might be what you need.
[PropertyPlaceholderConfigurer not loading programmatically
[1]:

Is it possible to #Lazy init a Spring #Value?

Is it possible to #Lazy init a Spring #Value?
e.g.
#Lazy
#Value("${someConfig}")
private String someConfig;
The scenario I'm specifically referring to, is a variable which is set via JNDI, and an embedded Tomcat container, which has some of it's JNDI variables initialised during Spring Boot loading... other scenarios I could think of where you'd want JIT variable population: It's "expensive" to retrieve a variable and you don't want to impact startup time, the variable isn't available at application startup, etc.
The above code gives the following error:
java.lang.IllegalArgumentException: Cannot subclass final class
java.lang.String
I imagine you could possibly achieve lazy-loaded variables by using a #ConfigurationProperties bean?
A follow up question: Can/Would a #Value-initialised variable be reinitialised (without an app restart), if the underlying variable source is changed (e.g. JNDI on the server)? i.e. re-retrieved
(I'm in the process of trying these last two scenarios)
You can give a try to such a setup. Downside is that it requires beans using this variable to be also declared as #Lazy.
#Bean(name = "myVar")
#Lazy
String foo(#Value("${someConfig}") String someConfig) {
return someConfig;
}
#Component
#Lazy
class SomeComponent {
#Autowired
#Qualifier("myVar")
String myVar;
}

Recover environment properties sent to EJB

I have a Delegate that instanciate the corresponding Bean sending credentials (Josso Authentication) through InitialContext as shown here.
At Bean, I've tried to recover Josso Data with SessionContext, as shown below:
#Resource private SessionContext context;
The problem I'm facing is that I couldn't retrieve Josso Data at Bean scope. I've tried "context.getEnvironment()" but this method is deprecated and I didn't find any alternative.
In order to find a solution, I've tried:
context.lookup(JNDI_BEAN_NAME);
context.lookup("java:comp/env/JNDI_BEAN_NAME")
context.lookup("java:comp/env")
But the two first commands only return me the Bean itself and the last one only return me global variables.
What is the correct alternative to "context.getEnvironment()"?
java:comp/env finds container-managed resources only and is read-only at runtime. If you want, you can expose a local interface that gets Josso credentials from the delegate.
#Local
public interface AuthenticatorLocal {
void getJossoCredentials();
}
Otherwise, you can just use context.getCallerPrincipal().

Set name of Spring Boot embedded database

How can I set the name of an embedded database started in a Spring Boot app running in a test?
I'm starting two instances of the same Spring Boot app as part of a test, as they collaborate. They are both correctly starting an HSQL database, but defaulting to a database name of testdb despite being provided different values for spring.datasource.name.
How can I provide different database names, or some other means of isolating the two databases? What is going to be the 'lightest touch'? If I can avoid it, I'd rather control this with properties than adding beans to my test config - the test config classes shouldn't be cluttered up because of this one coarse-grained collaboration test.
Gah - setting spring.datasource.name changes the name of the datasource, but not the name of the database.
Setting spring.datasource.url=jdbc:hsql:mem:mydbname does exactly what I need it to. It's a bit crap that I have to hardcode the embedded database implementation, but Spring Boot is using an enum for default values, which would mean a bigger rewrite if it were to try getting the name from a property.
You can try it so:
spring.datasource1.name=testdb
spring.datasource2.name=otherdb
And then declare datasource in your ApplicationConfig like this
#Bean
#ConfigurationProperties(prefix="spring.datasource1")
public DataSource dataSource1() {
...
}
#Bean
#ConfigurationProperties(prefix="spring.datasource2")
public DataSource dataSource2() {
...
}
See official docs for more details: https://docs.spring.io/spring-boot/docs/current/reference/html/howto-data-access.html#howto-configure-a-datasource

Passing variables to #Qualifier annotation in Spring

Is it possible to pass a variable to the #Qualifier annotation in Spring?
For example,
#Autowried
#Qualifier("datasource_" + "#{jobParameters['datasource.number']}")
private DataSource ds;
I have 10 different databases where my Spring batch job runs everyday. The database number is passed as a job parameter. I want to define the datasource to connect to based on the job parameter.
Thanks!
You are only allowed constant expressions in annotations.
So you are creating 10 data sources in your spring configuration - does your job need to use all ten in one run?? If you only need one connection for the lifetime of your spring context, can you just have 10 different sets of property files?
One thing you could do is to create all of your data sources in a map (keyed by "database number", then inject this map AND the key into your bean, for example...
public class MyBean {
#Autowired #Qualifier("dataSourceMap")
private Map<String, DataSource> dataSourceMap;
#Value("#{jobParameters['datasource.number']}")
private String dbKey;
public void useTheDataSource() {
DataSource ds = dataSourceMap.get(dbKey);
...
}
}
Or have I misunderstood?
no, you can't pass variables to any annotations in java. it has nothing to do with spring.
use a workaround. create and pass a service that will pick correct database each time it's needed

Categories