Spring boot application thread safe - java

I have a Spring-boot Java application which streams data continuously from Kafka and saves it to the database Cassandra after applying business logic.
Below are the pseudo classes and functions which resemble my application fully.
KafkaStreamer
#Configuration
#EnableKafka
public class KafkaStreamer {
private static final Logger LOGGER = LoggerFactory.getLogger(MyDomain.class);
#Autowired
private MyController myController;
#KafkaListener(topics = "${my-topic}", group = "${my-group}")
public void streamFromKafka(String payload) {
myController.processPayload(payload);
LOGGER.info("I am continously streaming data from Kafka "
+ "and forwarding it to controller for further processing...!");
}
}
MyController
#Controller
public class MyController {
private static final Logger LOGGER = LoggerFactory.getLogger(MyDomain.class);
#Autowired
private MyService myService;
public void processPayload(String payload) {
myService.applyBusinessLogic(payload);
LOGGER.info("Send to service for business-logic processing");
}
}
MyService
#Service
public class MyService {
private static final Logger LOGGER = LoggerFactory.getLogger(MyDomain.class);
#Autowired
private MyDomain myDomain;
public void applyBusinessLogic(String payload) {
myDomain.saveToDatabase(payload);
LOGGER.info("Applied business-logic");
}
}
MyDomain
#Repository
public class MyDomain {
private static final Logger LOGGER = LoggerFactory.getLogger(MyDomain.class);
#Autowired
private CassandraOperations cassandraTemplate;
/** The session. */
private Session session = null;
public void saveToDatabase(String payload) {
saveToTableA(payload);
saveToTableB(payload);
// Hello, I have saved data to database
LOGGER.info("Saved data to database");
}
private void saveToTableB(String payload) {
if (session == null)
session = cassandraTemplate.getSession();
session.execute(payload);
}
private void saveToTableA(String payload) {
if (session == null)
session = cassandraTemplate.getSession()
session.execute(payload);
}
}
The above pseudo code resembles my original application fully.
As you can see I do not have any class level variables other than logger, some auto-wired variable and cassandra session in MyDomain class
According to my knowledge, auto-wire by default in spring-boot is singleton.
I am passing payload (which is my message from Kafka) from one class to another class in function argument rather setting as the class level property of other class.
My question is,
Is my above application architecture or code thread safe ?.
Can Autowire create problem as by default it gives singleton reference of a class ( a point to note here is I do not have any class level variables other than logger and auto-wire variable)
If you feel if there exists a better way or anything, please feel free to write.
Many thanks from me.

Your solution stops to be thread-safe if you start to mutate the state of the shared object.
For example if you'd have a payload property in the MyDomain singleton Spring bean and set its value in the saveToDatabase and a bit later (even in the same method) consult it, that would be mutation and your code won't be thread-safe.
For example:
#Repository
public class MyDomain {
private String payload;
public void saveToDatabase(String payload) {
this.payload = payload;
saveToTableA();
saveToTableB();
}
private void saveToTableA() {
tableA.save(this.payload);
}
In this case when several threads call your saveToDatabase concurrently with different values, there is no guarantee what value you really will save to the DB in the saveToTableA().

Related

How can I use #RabbitListener in multiple classes?

I have a Spring application using RabbitMQ (spring-boot-starter-amqp).
I wanted to know if it's possible to use the #RabbitListener annotation across different classes.
I currently have two classes: Receiver and DeadLetterQueue
Receiver.java:
#Component
#Slf4j
public class Receiver {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
#RabbitListener(queues = "queueName")
public void receiveMessage(String message) throws Exception {
logger.info("Received <{}>" + message.toString());
throw new Exception("Error with the message");
}
DeadLetterQueue.java:
#Component
#Slf4j
public class DeadLetterQueue {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
#RabbitListener(queues = "otherQueueName")
public void processFailedMessages(String message) {
logger.info("Received failed message<{}>:" + message.toString());
}
}
RabbitMqConfig.java:
#Data
#Configuration
#ConfigurationProperties(prefix = "rabbitmq")
public class RabbitMqConfig {
private String host;
private int port;
private String username;
private String password;
private String queue;
private String exchange;
private String dlq;
private String dlx;
private String routingKey;
#Bean
Queue incomingQueue() {
return QueueBuilder.durable(queue)
.withArgument("x-dead-letter-exchange", dlx)
.build();
}
#Bean
FanoutExchange deadLetterExchange() {
return new FanoutExchange(dlx);
}
#Bean
Queue deadLetterQueue() {
return QueueBuilder.durable(dlq).build();
}
#Bean
Binding deadLetterBinding() {
return BindingBuilder.bind(deadLetterQueue()).to(deadLetterExchange());
}
#Bean
DirectExchange incomingQueueExchange() {
return new DirectExchange(exchange);
}
#Bean
Binding incomingQueueBinding() {
return BindingBuilder.bind(incomingQueue()).to(incomingQueueExchange()).with(queue);
}
When I use the RabbitMQ management tool to post a message to the FanoutExchange, the processFailedMessages inside the DeadLetterQueue class doesn't log anything to the console. However if this method is inside of the Receiver class, everything works fine. Which led me to the assumption that #RabbitListener couldn't work across multiple classes unless there is some configuration which I am missing.
Other information:
I'm using Docker to run the RabbitMQ Server
Strangely, if I put the receiveMessage() method inside the DeadLetterQueue class, the expectations fail.
So: Is it possible to define queues in multiple classes with #RabbitListener?
Yes, you can have as many #RabbitListeners as you want, either in the same class or in multiple classes, as long as those classes are used in Spring beans.
Since you have #Component on both, it should work just fine, unless the DeadLetterQueue is in a package that is not scanned by Spring Boot.
Boot only looks at the packages and subpackages where the main #SpringBootApplication is located.
You can enable DEBUG logging for org.springframework to log all the bean creation during application initialization.
I'm using Docker to run the RabbitMQ Server
The location of the broker is irrelevant.

In Spring Boot using SimpleThreadScope I get two instances of an object in the same thread

I define an object as with scope "thread".
In some place in the code the instance is obtained with #Autowired and in other place with context getBean(), when comparing the objects, they are different.
Since the object is "thread" scoped I was expecting the same instance of the object to be returned.
Following is the code, first I define a custom scope
#Configuration
public class ThreadScopeRegisteringBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.registerScope("thread", new SimpleThreadScope());
}
}
A test object is defined as:
#Component
#Scope("thread")
public class ThreadScopeObject implements InitializingBean {
private static final Logger LOGGER = LoggerFactory.getLogger(ThreadScopeObject.class);
private String field1;
public String getField1() {
return field1;
}
public void setField1(String field1) {
this.field1 = field1;
}
#Override
public void afterPropertiesSet() throws Exception {
LOGGER.info("****************** new object - " + this);
}
}
Also a service is defined as:
#Service
public class ThreadScopeService {
private static final Logger LOGGER = LoggerFactory.getLogger(ThreadScopeService.class);
#Autowired
private ThreadScopeObject threadScopeObject;
public void showObject() {
LOGGER.info ("ShowObject: " + threadScopeObject);
}
}
And finally a Async method is defined [RunnableService.java]:
#Async
public CompletableFuture<Map> objectInstanceTest() {
ThreadScopeObject o = ApplicationContextHolder.getContext().getBean(ThreadScopeObject.class);
LOGGER.info ("Thread object: " + o);
service.showObject();
return CompletableFuture.completedFuture(new HashMap<>());
}
When running the application I get the following log:
19:15:27.094 [mcd-async-1] INFO com.mono.threadSample.ThreadScopeObject - ****************** new object - com.mono.threadSample.ThreadScopeObject#69c8f2bb
19:15:27.094 [mcd-async-1] INFO com.mono.threadSample.RunnableService - Thread object: com.mono.threadSample.ThreadScopeObject#69c8f2bb
19:15:27.094 [mcd-async-1] INFO com.mono.threadSample.ThreadScopeService - ShowObject: com.mono.threadSample.ThreadScopeObject#fd0e5b6
I would like to know the reason why an object "thread" scoped is instantiated twice in the same thread.
Code: https://github.com/saavedrah/spring-threadSample
Thank you.
The ThreadScopeService is a singleton by default, so when it is constructed by spring it will get a ThreadScopeObject from the thread that created it and it won't be updated afterwards. There are two ways to solve this:
inject a Provider<ThreadScopeObject> or ObjectFactory<ThreadScopeObject> into ThreadScopeService and call their get methods to retrieve the scoped object when needed.
annotate the ThreadScopeObject with #Scope("thread", proxyMode = ScopedProxyMode.TARGET_CLASS). This will make spring create a proxy around your object, which will delegate all calls to the correctly scoped instance.

Issue with Spring Cacheable - Not injecting service

I'm using the latest version of spring and using the caching concept. My (rest) service class seems to not be injected with the caching annotation. If I remove them it works perfectly however I don't use cache which is not what I want.
Application:
#SpringBootApplication
#EnableCaching
public class MyApplication{
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
#Bean
public CacheManager cacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("MyCache")));
return cacheManager;
}
The service:
#CacheConfig(cacheNames = "MyCache")
#Service
public class MyServiceImpl implements MyService {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final String errormessage = "Error getting books";
#Autowired
private UserRepositoryCrud userRepository;
public MyServiceImpl () {
}
#Override
#Cacheable(value = "MyCache", key = "#description", unless = "#result?.size() > 0")
public final List<Books> getBooks(String description) {
logger.debug("Starting getBooksService");
//service implementation ...
(I also have a Restcontroler that only call this service)
When calling this method getBooks, I got a nullpointer on the logger, but when debugging I realize that everything is null, even the errormessage string...
If I remove the #Cacheable annotation, it then works but I do not have the cache working which is not what I want.
Do you know what can be wrong ?
Thanks a lot,
Guys I found the issue... because the method was final!!! I spent a lot of time to find this!

Why #Singleton over #ApplicationScoped in Producers?

LoggerProducer.java is a class used to produce Loggers to be injected in CDI beans with:
#Inject
Logger LOG;
Full code:
import javax.ejb.Singleton;
/**
* #author rveldpau
*/
#Singleton
public class LoggerProducer {
private Map<String, Logger> loggers = new HashMap<>();
#Produces
public Logger getProducer(InjectionPoint ip) {
String key = getKeyFromIp(ip);
if (!loggers.containsKey(key)) {
loggers.put(key, Logger.getLogger(key));
}
return loggers.get(key);
}
private String getKeyFromIp(InjectionPoint ip) {
return ip.getMember().getDeclaringClass().getCanonicalName();
}
}
QUESTION: can #Singleton be safely turned into #ApplicationScoped ?
I mean, why would anyone want an EJB here ? Are there technical reasons, since no transactions are involved, and (AFAIK) it would be thread-safe anyway ?
I'm obviously referring to javax.enterprise.context.ApplicationScoped, not to javax.faces.bean.ApplicationScoped.
The #Singleton annotation provides not only transaction but also thread-safety by default. So if you will replace it with #ApplicationScoped, you will loose the synchronization. So in order to make it properly you need to do like this:
#ApplicationScoped
public class LoggerProducer {
private final ConcurrentMap<String, Logger> loggers = new ConcurrentHashMap<>();
#Produces
public Logger getProducer(InjectionPoint ip) {
String key = getKeyFromIp(ip);
loggers.putIfAbsent(key, Logger.getLogger(key));
return loggers.get(key);
}
private String getKeyFromIp(InjectionPoint ip) {
return ip.getMember().getDeclaringClass().getCanonicalName();
}
}
Also you can make it completely without any scope if you make the map as static

How to use Singleton object in Spring?

I am newbie to Spring Framework.I have tried following example in spring.
#Path("/XZY")
#Service
#Transactional
public class XZY {
#Autowired
SampleDAO sampleDao;
#Autowired
TestDAO testDao;
#Autowired
XZYinterface xzyinterface;
#POST
#Produces("text/plain")
#Path("/checkservice")
public Response XZYservice(#FormParam("Code") String Code,
#FormParam("source") String source,
#FormParam("value") String value) {
//return xzyinterface.checkXYZService(Code,sourceName,source);
XZYinterface xyz = ServiceFactory.getXZY(999);
return xyz.checkXYZService(Code,sourceName,source);
}
}
The following code will use to create singleton object
public class Singleton {
private static sampleA sampleClassA=null;
private static SampleB sampleClassB=null;
public static XZYAbstract getXZY(long id){
if(id == 999){
if(sampleClass == null){
sampleClassA = new sampleA();
}
return sampleClass;
}
if(id == 9999){
sampleClassB = new sampleA();
}
return sampleClassB;
}
}
Interface
public interface XZYinterface {
Response XZYservice(String Code, String source,String value)
}
Abstract class and implements Interface
public class XZYAbstract implements XZYinterface {
public XZYAbstract(){
super();
}
#Autowired
SampleDAO sampleDao;
#Autowired
TestDAO testDao;
public Response checkXYZService(String Code,String source,String value){
String sample = sampleDao.getValue(code);
//..source code
}
}
The following class extends abstract class.
public class sampleA extends XZYAbstract {
//some methods.
}
If i run the application it throws following errors
SEVERE [com.sun.jersey.spi.container.ContainerResponse] The RuntimeException could not be mapped to a response, re-throwing to the HTTP container: java.lang.NullPointerException
at com.test.xyz.XZYAbstract.checkXYZService(XZYAbstract.java:112) [:]
at com.test.XYZ.XZYservice(XZY.java:140) [:]
If i call directly without singleton object, values are initialized properly using Auto wired (//return xzyinterface.checkXYZService(Code,sourceName,source);) and it's working fine.
Throw from singleton object, values(sampleDAo,testDao) are not initialized properly.
How to resolve this error?
The reason is quite trivial: it's because Spring is just a library, and not a change to the Java language. Spring doesn't instrument nor enhance constructors, so the only way to get initialized Spring bean is to get it from the Spring context.
If you call new Bean(), you becomes Bean instance untouched by Spring.
For the question how to use singleton bean: do nothing. Spring beans are Singletons by default. You can specify other scope via #org.springframework.beans.factory.config.Scope annotation. See for example #Scope("prototype") bean scope not creating new bean, how it works.

Categories

Resources