Spring cacheManager is not singleton? - java

I have some problems with an spring AnnotationConfigApplicationContext.
The main aim is to create a Spring Configuration which can be run on application server or standalone. Subtask to make subcontext from this one which will be used by another application on the same AppServer.
But have some trouble with cacheManager.
That's my code:
aka AbstractConfig
package org.zib.test.a;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Arrays;
#Configuration
#EnableCaching
public class ConfigA {
#SuppressWarnings("UnusedDeclaration")
public static ApplicationContext getContext() {
return ContextA.getInstance().getContext();
}
/**
* use {#link org.zib.test.b.ContextB}
*/
public static <T> T getBean(Class<T> bean) {
return ContextB.getInstance().getContext().getBean(bean);
}
/**
* use {#link ContextB}
*/
public static <T> T getBean(String name, Class<T> bean) {
return ContextB.getInstance().getContext().getBean(name, bean);
}
#Bean(name = "cacheManager")
public CacheManager cacheManager() {
// configure and return an implementation of Spring's CacheManager SPI
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("default")));
return cacheManager;
}
}
aka AbstractContext
package org.zib.test.a;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class ContextA {
private ApplicationContext applicationContext;
public static ContextA instance;
public static ContextA getInstance() {
synchronized (ContextA.class) {
if (instance == null)
instance = new ContextA();
}
return instance;
}
private ContextA() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(ConfigA.class);
applicationContext.registerShutdownHook();
applicationContext.refresh();
this.applicationContext = applicationContext;
}
public ApplicationContext getContext() {
return applicationContext;
}
}
Mode Config (for example, WebLogicConfig)
package org.zib.test.b;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.*;
import org.zib.test.a.ConfigA;
#Configuration
#EnableCaching
#Import(ConfigA.class)
public class ConfigB {
#SuppressWarnings("UnusedDeclaration")
public static ApplicationContext getContext() {
return ContextB.getInstance().getContext();
}
/**
* use {#link ContextB}
*/
public static <T> T getBean(Class<T> bean) {
return ContextB.getInstance().getContext().getBean(bean);
}
/**
* use {#link ContextB}
*/
public static <T> T getBean(String name, Class<T> bean) {
return ContextB.getInstance().getContext().getBean(name, bean);
}
}
context:
package org.zib.test.b;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.zib.test.a.ContextA;
public class ContextB {
private ApplicationContext applicationContext;
public static ContextB instance;
public static ContextB getInstance() {
synchronized (ContextB.class) {
if (instance == null)
instance = new ContextB();
}
return instance;
}
private ContextB() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(ConfigB.class);
applicationContext.setParent(ContextA.getInstance().getContext());
applicationContext.registerShutdownHook();
applicationContext.refresh();
this.applicationContext = applicationContext;
}
public ApplicationContext getContext() {
return applicationContext;
}
}
And final - module config:
package org.zib.test.c;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Arrays;
#Configuration
#EnableCaching
public class ConfigC {
#SuppressWarnings("UnusedDeclaration")
public static ApplicationContext getContext() {
return ContextC.getInstance().getContext();
}
/**
* use {#link ContextC}
*/
public static <T> T getBean(Class<T> bean) {
return ContextC.getInstance().getContext().getBean(bean);
}
/*DELTE BY SUGGESTION OF Tomasz Nurkiewicz
#Bean(name = "cacheManager")
public CacheManager cacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("default")));
return cacheManager;
}*/
}
and its context:
package org.zib.test.c;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.zib.test.a.ContextA;
import org.zib.test.b.ContextB;
public class ContextC {
private ApplicationContext applicationContext;
public static ContextC instance;
public static ContextC getInstance() {
synchronized (ContextC.class) {
if (instance == null)
instance = new ContextC();
}
return instance;
}
private ContextC() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(ConfigC.class);
applicationContext.setParent(ContextA.getInstance().getContext());
applicationContext.registerShutdownHook();
applicationContext.refresh();
this.applicationContext = applicationContext;
}
public ApplicationContext getContext() {
return applicationContext;
}
}
My test class:
package org.zib.test;
import org.springframework.cache.CacheManager;
import org.zib.test.a.ConfigA;
import org.zib.test.c.ConfigC;
public class Test {
public static void main(String[] args) {
Object cm1 = ConfigC.getBean(CacheManager.class);
Object cm2 = ConfigA.getBean(CacheManager.class);
if (cm1 == cm2)
System.out.println("equals");
else
System.out.println("unequal");
}
}
i've already spent a lot of time to solve this problem - will be happy if anyone will help me

If I understand correctly A is a parent context with two children: B and C. You define cacheManager twice: in parent ConfigA and in one child ConfigC. Different application contexts can have the same beans, that's why you get two different instances.
In other words if you ask for cacheManager from ConfigC, it returns the one defined in that context. But if you ask for it from ConfigB or ConfigA, it'll return the one from ConfigA (other instance).
They are still singletons, but within the scope of a single application context. BTW can you describe what you want to achieve with this pretty complex architecture?

Related

Secondary type dependency injection does not work in spring boot

As per the documentation, spring boot will automatically check the bean class object created in any classes annotated with #Configuration & will override the default bean of that class & return the object with any properties that are injected as it is defined. But when i test this application in junit, it does not return any value that is being injected. All my classes are defined in the same package My code is as below,
//Engine class
package com.test.simpletest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
#Component
public class Engine {
private String msg;
public Engine() {
System.out.println("Engine class is being called");
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
//Test configuration class
package com.test.simpletest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class TestConfiguration{
#Bean
public Engine engine() {
Engine eng = new Engine();
eng.setMsg("Message is being called");
return eng;
}
}
//Spring boot main app
package com.test.simpletest;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class SimpleTestExampleApplication {
public static void main(String[] args) {
SpringApplication.run(SimpleTestExampleApplication.class, args);
}
}
//JUnit Test class
package com.test.simpletest;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import
org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
#SpringBootTest
public class SimpleTestExampleApplicationTests {
#Autowired
private Engine engine;
#Test
public void contextLoads() {
engine.getMsg();
//Both above and below approach does not work
// ApplicationContext apx = new
AnnotationConfigApplicationContext(TestConfiguration.class);
// Engine engine = (Engine)apx.getBean(Engine.class);
// engine.getMsg();
}
}
Please help me in finding a solution to the above problem.
DemoApplication
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
Engine
public class Engine {
private String msg;
public Engine() {
System.out.println("Engine class is being called");
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
TestConfiguration
#Configuration
public class TestConfiguration {
#Bean
public Engine getEngine() {
Engine eng = new Engine();
eng.setMsg("Message is being called");
return eng;
}
}
DemoApplicationTests
#RunWith(SpringRunner.class)
#SpringBootTest
#Import(TestConfiguration.class)
public class DemoApplicationTests {
#Autowired
private Engine engine;
#Test
public void contextLoads() {
System.out.println("engine : " + engine.getMsg());
}
}
Output
Engine class is being called
engine : Message is being called
Can you please remove #Component from Engine class and try again. I guess it’s should work fine.

Environment properties not getting read in spring boot

I have a utility class to read the environment variables.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
#Component
public final class PropertyUtil {
/** The system environment. */
private static Environment environment;
public static String getConfigProp(final String key) {
return environment.getProperty(key);
}
#Autowired
public void setEnvironment(Environment environment) {
PropertyUtil.environment = environment;
}
}
And I use it in one bean while initialization. The problem is that it runs file if I deploy the war file on tomcat but if I run the same application as a spring boot application from eclipse, it does not read the environment properties and the return values are therefore null.
Any idea what could be the cause of this problem?
package com.myspringboot.controller;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
#Component
public class PropertyUtil implements EnvironmentAware {
/** The system environment. */
public Environment environment;
public String getConfigProp(String key) {
return environment.getProperty(key);
}
#Override
public void setEnvironment(Environment arg0) {
environment=arg0;
}
}
#Test
public void testProperty(){
/* String driver= System.getenv().get("spring.datasource.driverClassName");
System.out.println(driver);
System.out.println(PropertyUtil.environment);*/
String str= propertyUtil.getConfigProp("spring.datasource.driverClassName");
System.out.println(str);
}
package com.myspringboot.controller;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
#Component
public final class PropertyUtil implements EnvironmentAware {
/** The system environment. */
public static Environment environment;
public static String getConfigProp(String key) {
return environment.getProperty(key);
}
#Override
public void setEnvironment(Environment arg0) {
if(environment==null){
environment=arg0;
}
}
}

Spring getBeanNamesForType is not working well

I have simple context configuration
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.core.ResolvableType;
import java.util.Arrays;
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(DemoApplication.class, args);
String[] namesForType = ctx.getBeanNamesForType(ResolvableType.forClassWithGenerics(Transformer.class, Integer.class, String.class));
System.out.println("namesForType = " + Arrays.toString(namesForType));
}
#Bean
public Transformer<Integer, String> stringTransformer() {
return new Transformer<Integer, String>();
}
}
and simple class
package com.example;
public class Transformer<F, T> {
T transform(F from) {
return null;
}
}
When I start the application, output is namesForType = []
In case when creation of bean is changed into separated class like
#Bean
public Transformer<Integer, String> stringTransformer() {
return new Transformer<Integer, String>(){};
}
The output is namesForType = [stringTransformer]
That is correct behaviour. Before the #Bean annotation is used it will not be picked up in the spring context.
Probably the problem is in org.springframework.core.ResolvableType#isInstance in obj.getClass()
public boolean isInstance(Object obj) {
return (obj != null && isAssignableFrom(obj.getClass()));
}
and
public boolean isAssignableFrom(Class<?> other) {
return isAssignableFrom(forClass(other), null);
}
because forClass(...) create resolvable type Transformer<?, ?>
But i can't understand how
#Autowire
private Transformer<String, Integer> transformer;
can inject the correct bean.
Problem is already reported in Spring JIRA. Please vote for this issue.
https://jira.spring.io/browse/SPR-14118

Start thread at springboot application

I want to execute a java class (which contains a java thread I want to execute) after spring boot starts. My initial code:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
And here is is the code I want to execute at start:
public class SimularProfesor implements Runnable{
// Class atributes
// Constructor
public SimularProfesor() {
//Initialization of atributes
}
#Override
public void run() {
while(true) {
// Do something
}
}
}
How can I call this thread? This is what I'm supposed to do:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
// Call thread (constructor must be executed too)
}
}
Don't mess around with threads yourself. Spring (and also plain Java) has a nice abstraction for that.
First create a bean of the type TaskExecutor in your configuration
#Bean
public TaskExecutor taskExecutor() {
return new SimpleAsyncTaskExecutor(); // Or use another one of your liking
}
Then create a CommandLineRunner (although an ApplicationListener<ContextRefreshedEvent> would also work) to schedule your task.
#Bean
public CommandLineRunner schedulingRunner(TaskExecutor executor) {
return new CommandLineRunner() {
public void run(String... args) throws Exception {
executor.execute(new SimularProfesor());
}
}
}
You could of course make also your own class managed by spring.
Advantage of this is that Spring will also cleanup the threads for you and you don't have to think about it yourself. I used a CommandLineRunner here because that will execute after all beans have bean initialized.
Main Class SpringBoot
#SpringBootApplication
#EnableAsync
#Controller
public class ...
Example Class Controler
import javax.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;
#Component
public class ExecutorBase {
private static final Logger log = LoggerFactory.getLogger(ExecutorBase.class);
#Autowired
private TaskExecutor taskExecutor;
#Autowired
private ApplicationContext applicationContext;
private Boolean debug = true;
#PostConstruct
public void atStartup() {
ClasseTaskRunn classeTaskRunn = applicationContext.getBean(ClasseTaskRunn.class);
taskExecutor.execute(classeTaskRunn );
if (debug) {
log.warn("###### Startup ok");
}
}
}
Example Class Task Runnable
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
#Component
#Scope("application")
public class ClasseTaskRunn implements Runnable {
private static final Logger log = LoggerFactory.getLogger(ClasseTaskRunn.class);
#Autowired
ClasseDAO classeDAO;
#Override
public void run() {
longBackgorund();
}
protected void longBackgorund() {
while (test) {
if (debug) {
log.warn("###### DEBUG: " ... );
}
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

mongoDB connection returning null value

Below my code which is returning value as null.
ConfigurationFile.java
package config;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;
import com.mongodb.MongoClient;
#Configuration
public class ConfigurationFile {
private static MongoTemplate mongoTemplate;
public #Bean(name="mongoTemplate")
MongoTemplate mongoTemplate()throws Exception{
mongoTemplate = new MongoTemplate(new MongoClient("localhost",27017),"Test");
System.out.println("mongoTemplateValue1--> " + mongoTemplate);
return mongoTemplate;
}
public static MongoTemplate getMongoTemplate() {
System.out.println("mongoTemplateValue-->" + mongoTemplate);
return mongoTemplate;
}
}
Client.java
package client;
import java.net.UnknownHostException;
import org.springframework.data.mongodb.core.MongoTemplate;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.MongoClient;
import config.ConfigurationFile;
import extraction.Extractor;
public class Client {
private MongoTemplate mongoTemplate;
public static void main(String[] args){
Client c = new Client();
c.sample();
}
private void sample(){
SetupMongoDb();
}
private void SetupMongoDb() {
if (mongoTemplate == null) {
System.out.println("insideSetup");
mongoTemplate = ConfigurationFile.getMongoTemplate();
}
}
}
I am unable to get the mongoTemplate value. Below the output
insideSetup
mongoTemplateValue-->null
Can anyone please help on this?
Your mongoTemplate() method is never being called because you are not actually creating Spring Context when starting your application with Client.main().
Need to learn how Spring Framework works, specifically how to create application context. Then you'll need to point your context to your configuration file and use autowiring to obtain your MongoTemplate.
#Configuration
public class ConfigurationFile {
#Bean(name="mongoTemplate")
public MongoTemplate mongoTemplate()throws Exception{
MongoTemplate mongoTemplate = new MongoTemplate(new MongoClient("localhost",27017),"Test");
return mongoTemplate;
}
}
Then just use the autowired field:
#Service
public class SomeService {
#Autowired
private MongoTemplate mongoTemplate;
public doSomething() {
//Use your mongoTemplate
}
}

Categories