diff --git a/jans-core/message/pom.xml b/jans-core/message/pom.xml
new file mode 100644
index 00000000000..d2cd8afdd80
--- /dev/null
+++ b/jans-core/message/pom.xml
@@ -0,0 +1,84 @@
+
+
+ 4.0.0
+ jans-core-message
+ Caches support
+
+
+ io.jans
+ jans-core-parent
+ 1.0.20
+
+
+
+ ${maven.min-version}
+
+
+
+
+
+ src/main/resources
+ true
+
+ **/*.xml
+ **/services/*
+ **/*.properties
+
+
+
+
+
+ src/test/resources
+ true
+
+ **/*.xml
+ **/services/*
+ **/*.properties
+
+
+
+
+
+
+
+
+ ${project.groupId}
+ jans-core-cache
+
+
+ ${project.groupId}
+ jans-orm-core
+
+
+ ${project.groupId}
+ jans-orm-sql
+
+
+
+
+ jakarta.annotation
+ jakarta.annotation-api
+
+
+ jakarta.enterprise
+ jakarta.enterprise.cdi-api
+
+
+ jakarta.inject
+ jakarta.inject-api
+
+
+
+ redis.clients
+ jedis
+
+
+
+
+ org.testng
+ testng
+
+
+
+
+
\ No newline at end of file
diff --git a/jans-core/message/src/main/java/io/jans/service/BaseMessageService.java b/jans-core/message/src/main/java/io/jans/service/BaseMessageService.java
new file mode 100644
index 00000000000..9e9e0357579
--- /dev/null
+++ b/jans-core/message/src/main/java/io/jans/service/BaseMessageService.java
@@ -0,0 +1,68 @@
+/*
+ * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text.
+ *
+ * Copyright (c) 2023, Janssen Project
+ */
+
+package io.jans.service;
+
+import org.slf4j.Logger;
+
+import io.jans.service.message.MessageInterface;
+import io.jans.service.message.MessageProvider;
+import io.jans.service.message.pubsub.PubSubInterface;
+import jakarta.inject.Inject;
+
+/**
+ * Provides operations with messages
+ *
+ * @author Yuriy Movchan Date: 30/11/2023
+ */
+public abstract class BaseMessageService implements MessageInterface {
+
+ public static int DEFAULT_EXPIRATION = 60;
+
+ @Inject
+ private Logger log;
+
+ public void subscribe(PubSubInterface pubSubAdapter, String... channels) {
+ MessageProvider> messageProvider = getMessageProvider();
+ if (messageProvider == null) {
+ log.error("Message provider is invalid!");
+ return;
+ }
+
+ log.trace("Subscribe '{}' for channels '{}'", pubSubAdapter, channels);
+ messageProvider.subscribe(pubSubAdapter, channels);
+ }
+
+ public void unsubscribe(PubSubInterface pubSubAdapter) {
+ MessageProvider> messageProvider = getMessageProvider();
+ if (messageProvider == null) {
+ log.error("Message provider is invalid!");
+ return;
+ }
+
+ log.trace("Unsubscribe '{}'", pubSubAdapter);
+ messageProvider.unsubscribe(pubSubAdapter);
+ }
+
+ public boolean publish(String channel, String message) {
+ MessageProvider> messageProvider = getMessageProvider();
+ if (messageProvider == null) {
+ log.error("Message provider is invalid!");
+ return false;
+ }
+
+ if (log.isTraceEnabled()) {
+ log.trace("Publish '{}' to channel '{}'", message, channel);
+ }
+
+ boolean result = messageProvider.publish(channel, message);
+
+ return result;
+ }
+
+ protected abstract MessageProvider getMessageProvider();
+
+}
diff --git a/jans-core/message/src/main/java/io/jans/service/MessageService.java b/jans-core/message/src/main/java/io/jans/service/MessageService.java
new file mode 100644
index 00000000000..92e7bf13a06
--- /dev/null
+++ b/jans-core/message/src/main/java/io/jans/service/MessageService.java
@@ -0,0 +1,29 @@
+/*
+ * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text.
+ *
+ * Copyright (c) 2023, Janssen Project
+ */
+
+package io.jans.service;
+
+import io.jans.service.message.MessageProvider;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+
+/**
+ * Provides operations with messages
+ *
+ * @author Yuriy Movchan Date: 2023/12/03
+ */
+@ApplicationScoped
+public class MessageService extends BaseMessageService {
+
+ @Inject
+ private MessageProvider messageProvider;
+
+ @Override
+ protected MessageProvider getMessageProvider() {
+ return messageProvider;
+ }
+
+}
diff --git a/jans-core/message/src/main/java/io/jans/service/message/AbstractMessageProvider.java b/jans-core/message/src/main/java/io/jans/service/message/AbstractMessageProvider.java
new file mode 100644
index 00000000000..740ffbb69ea
--- /dev/null
+++ b/jans-core/message/src/main/java/io/jans/service/message/AbstractMessageProvider.java
@@ -0,0 +1,22 @@
+/*
+ * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text.
+ *
+ * Copyright (c) 2023, Janssen Project
+ */
+
+package io.jans.service.message;
+
+import java.util.concurrent.ExecutorService;
+
+/**
+ * Interface for each message provider
+ *
+ * @author Yuriy Movchan Date: 30/11/2023
+ */
+public abstract class AbstractMessageProvider extends MessageProvider {
+
+ public abstract void create(ExecutorService executorService);
+
+ public abstract void destroy();
+
+}
diff --git a/jans-core/message/src/main/java/io/jans/service/message/MessageInterface.java b/jans-core/message/src/main/java/io/jans/service/message/MessageInterface.java
new file mode 100644
index 00000000000..703ff55d228
--- /dev/null
+++ b/jans-core/message/src/main/java/io/jans/service/message/MessageInterface.java
@@ -0,0 +1,24 @@
+/*
+ * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text.
+ *
+ * Copyright (c) 2023, Janssen Project
+ */
+
+package io.jans.service.message;
+
+import io.jans.service.message.pubsub.PubSubInterface;
+
+/**
+ * Interface for each message provider
+ *
+ * @author Yuriy Movchan Date: 30/11/2023
+ */
+public interface MessageInterface {
+
+ void subscribe(PubSubInterface pubSubAdapter, String... channels);
+
+ void unsubscribe(PubSubInterface pubSubAdapter);
+
+ boolean publish(String channel, String message);
+
+}
diff --git a/jans-core/message/src/main/java/io/jans/service/message/MessageProvider.java b/jans-core/message/src/main/java/io/jans/service/message/MessageProvider.java
new file mode 100644
index 00000000000..466fb9e17c4
--- /dev/null
+++ b/jans-core/message/src/main/java/io/jans/service/message/MessageProvider.java
@@ -0,0 +1,27 @@
+/*
+ * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text.
+ *
+ * Copyright (c) 2023, Janssen Project
+ */
+
+package io.jans.service.message;
+
+import io.jans.service.message.model.config.MessageProviderType;
+
+/**
+ * Interface for each message provider
+ *
+ * @author Yuriy Movchan Date: 30/11/2023
+ */
+public abstract class MessageProvider implements MessageInterface {
+
+ /*
+ * Delegate internal connection object
+ */
+ public abstract T getDelegate();
+
+ public abstract MessageProviderType getProviderType();
+
+ public abstract void shutdown();
+
+}
diff --git a/jans-core/message/src/main/java/io/jans/service/message/MessageProviderFactory.java b/jans-core/message/src/main/java/io/jans/service/message/MessageProviderFactory.java
new file mode 100644
index 00000000000..c6bf7c4d91c
--- /dev/null
+++ b/jans-core/message/src/main/java/io/jans/service/message/MessageProviderFactory.java
@@ -0,0 +1,127 @@
+/*
+ * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text.
+ *
+ * Copyright (c) 2023, Janssen Project
+ */
+
+package io.jans.service.message;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+
+import org.slf4j.Logger;
+
+import io.jans.service.message.model.config.MessageConfiguration;
+import io.jans.service.message.model.config.MessageProviderType;
+import jakarta.annotation.PostConstruct;
+import jakarta.annotation.PreDestroy;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.inject.Any;
+import jakarta.enterprise.inject.Instance;
+import jakarta.enterprise.inject.Produces;
+import jakarta.inject.Inject;
+
+/**
+ * Message provider factory
+ *
+ * @author Yuriy Movchan Date: 30/11/2023
+ */
+@ApplicationScoped
+public class MessageProviderFactory {
+
+ public static final String MESSAGE_PROVIDER_THREAD_NAME = "MessageProviderThread";
+
+ @Inject
+ private Logger log;
+
+ @Inject
+ private MessageConfiguration messageConfiguration;
+
+ @Inject
+ @Any
+ private Instance instance;
+
+ private ExecutorService executorService;
+ private MessageProvider messageProvider;
+
+ @PostConstruct
+ public void create() {
+ executorService = Executors.newCachedThreadPool(new ThreadFactory() {
+ public Thread newThread(Runnable runnable) {
+ Thread thread = new Thread(runnable);
+ thread.setName(MESSAGE_PROVIDER_THREAD_NAME);
+ thread.setDaemon(true);
+ return thread;
+ }
+ });
+ }
+
+ @PreDestroy
+ public void destroy() {
+ shutdown();
+ }
+
+ @Produces
+ @ApplicationScoped
+ public MessageProvider getMessageProvider() {
+ log.debug("Started to create message provider");
+
+ messageProvider = getCacheProvider(messageConfiguration);
+
+ return messageProvider;
+ }
+
+ public MessageProvider getCacheProvider(MessageConfiguration messageConfiguration) {
+ MessageProviderType messageProviderType = messageConfiguration.getMessageProviderType();
+
+ // Create proxied bean
+ AbstractMessageProvider> messageProvider = null;
+ switch (messageProviderType) {
+ case NULL:
+ messageProvider = instance.select(NullMessageProvider.class).get();
+ break;
+ case REDIS:
+ messageProvider = instance.select(RedisMessageProvider.class).get();
+ break;
+ case POSTGRES:
+ messageProvider = instance.select(PostgresMessageProvider.class).get();
+ break;
+ }
+
+ if (messageProvider == null) {
+ throw new RuntimeException(
+ "Failed to initialize messageProvider, messageProvider is unsupported: " + messageProviderType);
+ }
+
+ messageProvider.create(executorService);
+
+ return messageProvider;
+ }
+
+ public int getActiveCount() {
+ return ((ThreadPoolExecutor) executorService).getActiveCount();
+ }
+
+ public int getPoolSize() {
+ return ((ThreadPoolExecutor) executorService).getPoolSize();
+ }
+
+ public void shutdown() {
+ if (messageProvider != null) {
+ log.info("Starting message provider shutdown...");
+ messageProvider.shutdown();
+ messageProvider = null;
+ }
+
+ if (executorService != null) {
+ log.info("Starting message provider thread pool shutdown...");
+ executorService.shutdownNow();
+ executorService = null;
+ }
+
+ log.info("Successfully stopped message provider pool");
+ }
+
+}
diff --git a/jans-core/message/src/main/java/io/jans/service/message/NullMessageProvider.java b/jans-core/message/src/main/java/io/jans/service/message/NullMessageProvider.java
new file mode 100644
index 00000000000..48655ef6400
--- /dev/null
+++ b/jans-core/message/src/main/java/io/jans/service/message/NullMessageProvider.java
@@ -0,0 +1,88 @@
+/*
+ * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text.
+ *
+ * Copyright (c) 2023, Janssen Project
+ */
+
+package io.jans.service.message;
+
+import java.util.concurrent.ExecutorService;
+
+import org.slf4j.Logger;
+
+import io.jans.service.cache.RedisConfiguration;
+import io.jans.service.message.model.config.MessageConfiguration;
+import io.jans.service.message.model.config.MessageProviderType;
+import io.jans.service.message.pubsub.PubSubInterface;
+import io.jans.util.security.StringEncrypter;
+import jakarta.annotation.PostConstruct;
+import jakarta.annotation.PreDestroy;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+
+/**
+ * Null message provider
+ *
+ * @author Yuriy Movchan Date: 30/11/2023
+ */
+@ApplicationScoped
+public class NullMessageProvider extends AbstractMessageProvider