Singleton behavior not achieved - spring

I have a #Component like below:
#Component
public class FilenetConnection {
#Value("${filenet.url}")
String url;
#Value("${filenet.username}")
String username;
#Value("${filenet.password}")
String password;
#Bean
public Connection getCPEConnection() {
try {
Connection conn = Factory.Connection.getConnection(url);
Subject subject = UserContext.createSubject(conn, username, password, "FileNetP8WSI");
UserContext uc = UserContext.get();
uc.pushSubject(subject);
System.out.println("CE Connection" + conn);
return conn;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
}
And in my RestController this is how I am trying to access the Bean method getCPEConnection() return value:
#Autowired
ConfigurableApplicationContext applicationContext;
public FilenetConnection getBeanOfBaseComponent() {
return applicationContext.getBean(FilenetConnection.class);
}
Now everytime I access the bean method's return value using getBeanOfBaseComponent().getCPEConnection() a new object for conn from the (Singleton) #Bean getCPEConnection() is returned. What I am missing here?

So I understand that you do something like this:
You call getBeanOfBaseComponent() and there you get the Instance of FilenetConnection and on this instance you call getCPEConnection().
If I understood this correct it makes sense that it doesn't work.
Because you don't use the bean you just call a normal method which returns you a new Instance of Connenction.
So I don't use this way of accessing beans myself but I guess you need to use applicationContext.getBean(Connection.class); to be able to use the Connection bean.
Or another and easier solution would be to just inject the Connection into your Controller class.
And normally you define beans in classes annotated with #Configuration and not #Component

Related

How to create a conditional bean in Spring

I need to create a Conditional Bean in Spring. The use case is as following:
Class 1
In this class we are trying to create the Bean, which should be created for some clients who have the required permission, and for others it will return empty(). Thus the application should boot-up for all the clients without the BeanCreationException
#org.springframework.context.annotation.Configuration
public class SomeBeanConfiguration {
#Bean
public Optional<SomeBean> someBean() {
// whoAmI() ? returns IAmClient_1 - for whom this bean should be created
// whoAmI() ? returns IAmClient_2 - for whom this bean should not be created
final String somePermission = whoAmI();
try {
return Optional.of(SomeBean.builder()
.withPermission(new SomeCredentialsProvider(somePermission))
.build());
} catch (Exception ex) {
LOG.error("SomeBean creation exception : ", ex);
}
return Optional.empty();
}
}
Class 2
Where we are using this Bean in Constructor injection
#Bean
public SomeHelper someHelper(Optional<SomeBean> someBean) {
return new someHelper(someBean);
}
But the someHelper for client, who have permission are also getting an Optional.empty() in constructor.
What I am doing wrong here? Can anyone please help?
You need to change your method that's creating the bean. It should not be returning a bean of type Optional, it should be returning a bean of type SomeBean. Also, consider rewriting your logic to something more understandable, like dropping the catch block and creating the bean based on the output of whoAmI().
#Bean
public SomeBean someBean() {
// whoAmI() ? returns IAmClient_1 - for whom this bean should be created
// whoAmI() ? returns IAmClient_2 - for whom this bean should not be created
String somePermission = whoAmI();
if (somePermission.equals("IAmClient_1") {
return SomeBean.builder().withPermission(newSomeCredentialsProvider(somePermission)).build());
} else {
return null;
}
}
Now, when you autowire the Optional, the optional will contain the bean for IAmClient_1, and will be empty for all other cases.
In my opinion, it would be better to always construct SomeBean and just modify its behavior based on the value of the permission you're checking, but that's up to you.

what the difference between the two codes (Spring Boot)?

These two codes should do exactly the same thing, but the first one works and the second one doesnt work. Can anyone review the code and give the details about why the code failed during second approach.
The first code :
#Component
public class AdminSqlUtil implements SqlUtil {
#Autowired private ApplicationContext context;
DataSource dataSource =(DataSource) context.getBean("adminDataSource");
public void runSqlFile(String SQLFileName) {
Resource resource = context.getResource(SQLFileName);
EncodedResource encodedResource = new EncodedResource(resource, Charset.forName("UTF-8"));
try {
ScriptUtils.executeSqlScript(dataSource.getConnection(), encodedResource);
} catch (SQLException ex) {
throw new RuntimeException(ex);
}
}
The second code :
#Component
public class AdminSqlUtil implements SqlUtil {
#Autowired private ApplicationContext context;
public void runSqlFile(String SQLFileName) {
Resource resource = context.getResource(SQLFileName);
EncodedResource encodedResource = new EncodedResource(resource, Charset.forName("UTF-8"));
try {
ScriptUtils.executeSqlScript((DataSource)context.getBean("adminDataSource").getConnection(), encodedResource);
} catch (SQLException ex) {
throw new RuntimeException(ex);
}
}
The first one has a private scope and the framework can not access it. You could have add #inject before your private scope variable so the framework can initialize it. However the best practice is to define a public dependency setter for that to work.
The second one on the other hand initiates the value at the start, which is not a dependency injection by the way. I am not talking about good and bad practice. It is wrong. We don’t initialize a variable which is suppose to be initialized by the framework.
So lets go with the first one, Try to add a setter for it.
Take a look at this link.

java.lang.NullPointerException: When saving jpa data using Gemfire cachewriter

Jpa Repository save is working in all classes. But when trying to save in CacheWriter it is throwing NullPointerException(personRepository.save(entryEvent.getNewValue())). Any idea on this? Configured mysql database in application properties.
java.lang.NullPointerException
at com.javasampleapproach.gemfirerestapi.GemfireWriter.beforeCreate(GemfireWriter.java:28)
at com.gemstone.gemfire.internal.cache.LocalRegion.cacheWriteBeforePut(LocalRegion.java:3131)
at com.gemstone.gemfire.internal.cache.AbstractRegionMap.invokeCacheWriter(AbstractRegionMap.java:3145)
at com.gemstone.gemfire.internal.cache.AbstractRegionMap.basicPut(AbstractRegionMap.java:2909)
at com.gemstone.gemfire.internal.cache.LocalRegion.virtualPut(LocalRegion.java:5821)
at com.gemstone.gemfire.internal.cache.LocalRegionDataView.putEntry(LocalRegionDataView.java:118)
at com.gemstone.gemfire.internal.cache.LocalRegion.basicPut(LocalRegion.java:5211)
at com.gemstone.gemfire.internal.cache.LocalRegion.validatedPut(LocalRegion.java:1597)
at com.gemstone.gemfire.internal.cache.LocalRegion.put(LocalRegion.java:1580)
at com.gemstone.gemfire.internal.cache.AbstractRegion.put(AbstractRegion.java:327)
Controller:
#GetMapping(value = "/getPerson")
public Iterable<Person> getPerson(#RequestParam("id") long personId,#RequestParam("age") int age, #RequestParam("name") String name) {
try{
Person bob = new Person();
bob.setPersonId(personId);
bob.setAge(age);
bob.setName(name);
Region<Long,Person> region=gemfireCache.getRegion("person");
region.put(personId, bob);
}catch(Exception e){
e.printStackTrace();
}
return personRepository.findAll();
}
Cachewriter:
public class GemfireCacheWriter implements CacheWriter<Long, Person>{
#Autowired
PersonRepository personRepository;
#Override
public void beforeCreate(EntryEvent<Long, Person> entryEvent) throws CacheWriterException {
// TODO Auto-generated method stub
personRepository.save(entryEvent.getNewValue());
}
}
CacheWriter Config:
#Bean
LocalRegionFactoryBean<Long, Person> personRegion(final GemFireCache cache) {
LocalRegionFactoryBean<Long, Person> personRegion = new LocalRegionFactoryBean<>();
personRegion.setCache(cache);
personRegion.setName("person");
personRegion.setPersistent(false);
personRegion.setCacheWriter(new GemfireWriter());
personRegion.setCacheLoader(new GemfireLoader());
return personRegion;
}
Looking at the source code for LocalRegion, I don't think the entryEvent received by your CacheWriter could be null, so the actual null reference is probably personRepository. Have you correctly configured spring-data-gemfire to autowire the PersonRepository?, is the CacheWriter configured as a Spring bean (using the #Component as an example)?.
You can use the Write Through Example as a good starting point for implementing this use case.
Hope this helps. Cheers.

Can I programmatically add a qualifier to a bean?

I am registering transaction managers in my code, I would normally use annotation based configuration but as I don't know until runtime how many data sources (and hence transaction managers) there will be, I have to programmatically register these, as follows:
private final void registerTransactionManagerBean(final DataSource dataSource, ConfigurableApplicationContext context) {
String transactionManagerName = this.getName() + "-transactionManager";
context.getBeanFactory().registerSingleton(transactionManagerName, new DataSourceTransactionManager(dataSource));
LOG.info("Registering transaction manager under name : " + transactionManagerName);
}
Assuming this.getName() returned 'mydb', I originally expected to be able to qualify a transaction manager like this:
#Transactional("mydb-transactionManager")
What I've realised however is the value of that annotation refers to the qualifier and not the name. I did a quick test by declaring a bean as below and it works:
#Bean
#Qualifier("mydb-transactionManager")
public PlatformTransactionManager test() {
return new DataSourceTransactionManager(new EmbeddedDatabaseBuilder().build());
}
My question is, is there a way I can programmatically add a qualifier when registering a bean?
UPDATE
I've worked this out, I'm falling foul of this problem (in BeanFactoryAnnotationUtils:isQualifierMatch):
catch (NoSuchBeanDefinitionException ex) {
// ignore - can't compare qualifiers for a manually registered singleton object
}
I am manually registering my transaction manager bean so I presume this is why I'm stuck. I'm not really sure what options that gives me apart from to not programmatically register transaction managers as a runtime thing sadly.
I've worked this out, I'm falling foul of this problem:
catch (NoSuchBeanDefinitionException ex) {
// ignore - can't compare qualifiers for a manually registered singleton object
}
I am manually registering my transaction manager bean so I presume this is why I'm stuck. I'm not really sure what options that gives me apart from to not programatically register transaction managers as a runtime thing sadly.
Raised as a JIRA issue - https://jira.spring.io/browse/SPR-11915
public class RuntimeRegistrationWithQualifierTest {
private AnnotationConfigApplicationContext context;
#Test
public void beanWithQualifier() {
final GenericBeanDefinition helloBeanDefinition = new GenericBeanDefinition();
helloBeanDefinition.addQualifier(new AutowireCandidateQualifier(Hello.class));
final GenericBeanDefinition worldBeanDefinition = new GenericBeanDefinition();
worldBeanDefinition.addQualifier(new AutowireCandidateQualifier(World.class));
final DefaultListableBeanFactory factory = context.getDefaultListableBeanFactory();
factory.registerBeanDefinition("helloBean", helloBeanDefinition);
factory.registerSingleton("helloBean", "hello");
factory.registerBeanDefinition("worldBean", worldBeanDefinition);
factory.registerSingleton("worldBean", "world");
context.register(Foo.class);
context.refresh();
final Foo foo = context.getBean(Foo.class);
assertThat(foo.hello).isEqualTo("hello");
assertThat(foo.world).isEqualTo("world");
}
#Before
public void newContext() {
context = new AnnotationConfigApplicationContext();
}
#Qualifier
#Retention(RUNTIME)
#Target({FIELD, PARAMETER})
#interface Hello {}
#Qualifier
#Retention(RUNTIME)
#Target({FIELD, PARAMETER})
#interface World {}
static class Foo {
final String hello;
final String world;
Foo(#Hello final String hello, #World final String world) {
this.hello = hello;
this.world = world;
}
}
}

CMT rollback : how to roll back the transaction

The following code does not help in roll back even if I throw null pointer exception at update() method. Everytime it inserts values into the database if I run the code. Please help me how can I roll back the transaction if null pointer is thrown at update() method. Am I missing something in the code?
#TransactionManagement(value = TransactionManagementType.CONTAINER)
public class Bean implements RemoteIF {
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public void insertIntoDb() {
insert();
update();
}
private Integer update() {
val=0;
try {
Connection con = DbConn.getConnection();
Statement st = con.createStatement();
val1 = st.executeUpdate("INSERT INTO tab VALUES('ab')");
st.close();
throw new NullPointerException();
} catch (Exception e) {
System.out.println(e);
}
return val;
}
private Integer insert() {
int val = 0;
try {
Connection con = DbConn.getConnection();
Statement st = con.createStatement();
val = st.executeUpdate("INSERT INTO tab VALUES('bnm')");
st.close();
} catch (Exception e) {
System.out.println(e);
}
return val;
}
}
Couple things that stick out at me as suspect.
No #Stateless, #Stateful or #Singleton annotation on the Bean class. Unless you've declared the bean in an ejb-jar.xml file, then this is not getting recognized as an EJB. Definitely double check that.
The DbConn.getConnection() looks suspiciously like you might be trying to manage database connections yourself. If you have any code that uses the DriverManager or does new FooDataSource(), then that is definitely the problem. If you want transaction management to work you have to get all resources from the container via either
Injection via a #Resource DataSource datasource field in the EJB class
JNDI lookup of java:comp/env/yourDataSource, where yourDataSource is the name of a datasource you configured in the ejb-jar.xml or declared on the bean class via using #Resource(type=DataSource.class, name="youDataSource") -- that annotation goes on the class itself rather than a method or field.
See also these answers for some insight as to how transaction management works:
How does UserTransaction propagate?
Programming BMT - UserTransaction

Resources