I want to re-create (new Object) a specific bean at Runtime (no restarting the server) upon some DB changes. This is how it looks -
#Component
public class TestClass {
#Autowired
private MyShop myShop; //to be refreshed at runtime bean
#PostConstruct //DB listeners
public void initializeListener() throws Exception {
//...
// code to get listeners config
//...
myShop.setListenersConfig(listenersConfig);
myShop.initialize();
}
public void restartListeners() {
myShop.shutdownListeners();
initializeListener();
}
}
This code does not run as myShop object is created by Spring as Singleton & its context does not get refreshed unless the server is restarted. How to refresh (create a new object) myShop ?
One bad way I can think of is to create new myShop object inside restartListeners() but that does not seem right to me.
In DefaultListableBeanFactory you have public method destroySingleton("beanName")so you can play with it, but you have to be aware that if your autowired your bean it will keep the same instance of the object that has been autowired in the first place, you can try something like this:
#RestController
public class MyRestController {
#Autowired
SampleBean sampleBean;
#Autowired
ApplicationContext context;
#Autowired
DefaultListableBeanFactory beanFactory;
#RequestMapping(value = "/ ")
#ResponseBody
public String showBean() throws Exception {
SampleBean contextBean = (SampleBean) context.getBean("sampleBean");
beanFactory.destroySingleton("sampleBean");
return "Compare beans " + sampleBean + "=="
+ contextBean;
//while sampleBean stays the same contextBean gets recreated in the context
}
}
It is not pretty but shows how you can approach it. If you were dealing with a controller rather than a component class, you could have an injection in method argument and it would also work, because Bean would not be recreated until needed inside the method, at least that's what it looks like. Interesting question would be who else has reference to the old Bean besides the object it has been autowired into in the first place,because it has been removed from the context, I wonder if it still exists or is garbage colected if released it in the controller above, if some other objects in the context had reference to it, above would cause problems.
We have the same use-case. As already mentioned one of the main issues with re-creating a bean during runtime is how to updating the references that have already been injected. This presents the main challenge.
To work around this issue I’ve used Java’s AtomicReference<> class. Instead of injecting the bean directly, I’ve wrapped it as an AtomicReference and then inject that. Because the object wrapped by the AtomicReference can be reset in a thread safe manner, I am able to use this to change the underlying object when a database change is detected. Below is an example config / usage of this pattern:
#Configuration
public class KafkaConfiguration {
private static final String KAFKA_SERVER_LIST = "kafka.server.list";
private static AtomicReference<String> serverList;
#Resource
MyService myService;
#PostConstruct
public void init() {
serverList = new AtomicReference<>(myService.getPropertyValue(KAFKA_SERVER_LIST));
}
// Just a helper method to check if the value for the server list has changed
// Not a big fan of the static usage but needed a way to compare the old / new values
public static boolean isRefreshNeeded() {
MyService service = Registry.getApplicationContext().getBean("myService", MyService.class);
String newServerList = service.getPropertyValue(KAFKA_SERVER_LIST);
// Arguably serverList does not need to be Atomic for this usage as this is executed
// on a single thread
if (!StringUtils.equals(serverList.get(), newServerList)) {
serverList.set(newServerList);
return true;
}
return false;
}
public ProducerFactory<String, String> kafkaProducerFactory() {
Map<String, Object> configProps = new HashMap<>();
configProps.put(ProducerConfig.CLIENT_ID_CONFIG, "...");
// Here we are pulling the value for the serverList that has been set
// see the init() and isRefreshNeeded() methods above
configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, serverList.get());
configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
return new DefaultKafkaProducerFactory<>(configProps);
}
#Bean
#Lazy
public AtomicReference<KafkaTemplate<String, String>> kafkaTemplate() {
KafkaTemplate<String, String> template = new KafkaTemplate<>(kafkaProducerFactory());
AtomicReference<KafkaTemplate<String, String>> ref = new AtomicReference<>(template);
return ref;
}
}
I then inject the bean where needed, e.g.
public MyClass1 {
#Resource
AtomicReference<KafkaTemplate<String, String>> kafkaTemplate;
...
}
public MyClass2 {
#Resource
AtomicReference<KafkaTemplate<String, String>> kafkaTemplate;
...
}
In a separate class I run a scheduler thread that is started when the application context is started. The class looks something like this:
class Manager implements Runnable {
private ScheduledExecutorService scheduler;
public void start() {
scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(this, 0, 120, TimeUnit.SECONDS);
}
public void stop() {
scheduler.shutdownNow();
}
#Override
public void run() {
try {
if (KafkaConfiguration.isRefreshNeeded()) {
AtomicReference<KafkaTemplate<String, String>> kafkaTemplate =
(AtomicReference<KafkaTemplate<String, String>>) Registry.getApplicationContext().getBean("kafkaTemplate");
// Get new instance here. This will have the new value for the server list
// that was "refreshed"
KafkaConfiguration config = new KafkaConfiguration();
// The set here replaces the wrapped objet in a thread safe manner with the new bean
// and thus all injected instances now use the newly created object
kafkaTemplate.set(config.kafkaTemplate().get());
}
} catch (Exception e){
} finally {
}
}
}
I am still on the fence if this is something I would advocate doing as it does have a slight smell to it. But in limited and careful usage it does provide an alternate approach to the stated use-case. Please be aware that from a Kafka standpoint this code example will leave the old producer open. In reality one would need to properly do a flush() call on the old producer to close it. But that's not what the example is meant to demonstrate.
Related
Let's imagine I have the next classes in the project based on Spring framework:
interface I {
String getName()
}
#Component
class I1 implements I {
#Override
String getName() {return "I1"}
}
#Component
class I2 implements I {
#Override
String getName() {return "I1"}
}
And I want to gather them all in the map using the #Autowired method:
#Component
public class A {
private Map<I> map = new HashMap<>()
#Autowired
public registerI(I i) {
map.put(i.getName(), i)
}
}
Should I make this method registerI synchronized? I mean, can Spring call this method in several threads simultaneously? Or this method will be called sequentially?
Thanks
You don't have to use synchronized because Spring bean initialization is single-threaded and thread-safe. You can think of gotchas like thread-scoped or lazy beans but for regular singleton beans initialization happens in one thread.
You might want to use synchronized to make sure that after registerI() method is called your object is safely published, although auto-wired constructor with final field is more readable.
#Component
public class A {
private final Map<String, I> map;
public A(List<I> list) {
map = list.stream().collect(Collectors.toMap(I::getName, i -> i));
}
}
You will get an exception during app startup because Spring cannot determine the correct implementation of interface "I" what you want to inject. You should use #Qualifier.
If you want to accomplish that scenario, this should be enough.
#Component
public static class A {
private Map<String,I> map = new HashMap<>();
public A(List<I> list) {
//map = list.stream().collect(Collectors.toMap(I::getName, x -> x));
for (I i : list) {
map.put(i.getName(), i);
}
}
}
You will end with only one value in the map.
The commented line works if there are not duplicate map keys.
You can autowire context and get all the interested beans from it in a #PostConstruct method and create a hashmap with it.
Or
If you want that Map to be shared amongst multiple classes, make it a #Bean
#Configuration
class SomeConfig{
#Autowire Context context;
#Bean(name = "mapBean")
public Map<String, MyCustomClassName1> mapBean() {
Map<String, MyCustomClassName1> map = new HashMap<>();
//populate the map here - from Context
return map;
}
}
Spring fills List by your beans. After you can create map in postConstruct
#Component
public class A {
#Autowired
private List<I> list;
#Autowired
private Map<String, I> map;
#PostConstruct
private void init(){
map = list.stream()
.collect(Collectors.toMap(I::getName, element->element);
}
}
I need to build mappings for classes (literally a Map<Class<?>, String>), which won't vary at runtime, and keeping things decoupled is a priority. Since I'm in a Spring application, I thought I'd use an annotation and ClassPathScanningCandidateComponentProvider more or less like so:
#Inherited
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface Mapping {
String value();
}
And:
public class MappingLookUp {
private static final Map<Class<?>, String> MAPPING_LOOK_UP;
static {
Map<Class<?>, String> lookUp = new HashMap<>();
ClassPathScanningCandidateComponentProvider scanningCandidateComponentProvider = new ClassPathScanningCandidateComponentProvider(false);
scanningCandidateComponentProvider.addIncludeFilter(new AnnotationTypeFilter(Mapping.class));
for (BeanDefinition beanDefinition : scanningCandidateComponentProvider.findCandidateComponents("blah")) {
Class<?> clazz;
try {
clazz = Class.forName(beanDefinition.getBeanClassName());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
Mapping mapping = AnnotationUtils.getAnnotation(clazz, Mapping.class);
if (mapping == null) {
throw new IllegalStateException("This should never be null");
}
lookUp.put(clazz, mapping.value());
}
MAPPING_LOOK_UP = Collections.unmodifiableMap(lookUp);
}
public static String getMapping(Class<?> clazz) {
...
}
}
Although I believe this will work, this feels like:
a lot to put in a static initialization
a hacky use of the scanning component provider, even though it's commonly recommended for this purpose; BeanDefinition makes it sound like it's intended for finding Spring beans rather than general class definitions.
To be clear, the annotated values are data classes -- not Spring-managed beans -- so a BeanPostProcessor pattern doesn't fit, and indeed, that's why it feels awkward to use the scanning component provider that, to me, seems intended for discovery of Spring managed beans.
Is this the proper way to be implementing this pattern? Is it a proper application of the provider? Is there a feasible alternative without pulling in other classpath scanning implementations?
I will suggest this doesn't look like it is done in a very Spring-y way.
If I were to be doing this, I would utilize Spring's BeanPostProcessor or BeanFactoryPostProcessor. Both of these allow for introspection on all Bean's in Spring's BeanFactory, and would allow you to get away from the static-ness of your current setup, as the PostProcessors are just Spring Bean's themselves.
class MappingLookup implements BeanPostProcessor {
private final Map<Class<?>, String> lookup = new HashMap<>();
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
// check bean's class for annotation...
// add to lookup map as necessary...
// make sure to return bean (javadoc explains why)
return bean;
}
public String getMapping(Class<?> clazz) {
// ...
}
// omitted other methods...
}
I asked a very similar question recently How to get list of Interfaces from #ComponentScan packages and finally implemented the first of suggested approaches.
You can see the code https://github.com/StanislavLapitsky/SpringSOAProxy see https://github.com/StanislavLapitsky/SpringSOAProxy/blob/master/core/src/main/java/org/proxysoa/spring/service/ProxyableScanRegistrar.java and of course initialization annotation https://github.com/StanislavLapitsky/SpringSOAProxy/blob/master/core/src/main/java/org/proxysoa/spring/annotation/ProxyableScan.java the key thing is to add #Import({ProxyableScanRegistrar.class})
The key code is
public class ProxyableScanRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {
private Environment environment;
#Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
#Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// Get the ProxyableScan annotation attributes
Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(ProxyableScan.class.getCanonicalName());
if (annotationAttributes != null) {
String[] basePackages = (String[]) annotationAttributes.get("value");
if (basePackages.length == 0) {
// If value attribute is not set, fallback to the package of the annotated class
basePackages = new String[]{((StandardAnnotationMetadata) metadata).getIntrospectedClass().getPackage().getName()};
}
In my application i want to initialize the bean before hitting application url, and store the common values for drop-down. here it is declaration for bean
<beans:bean id="listService" class="com.system.beans.DropDownList"
init-method="populateMasterList" scope="application"/>
Bean:
public class DropDownList implements InitializingBean
{
private static final Logger logger = LoggerFactory.getLogger(DropDownList.class);
public static Map<String, Map<Integer, String>> listMap = new HashMap<String, Map<Integer, String>>();
#Autowired
private static SystemService systemService;
#Autowired(required = true)
#Qualifier(value = "systemService")
public void setSystemService(SystemService systemService)
{
this.systemService = systemService;
}
#PostConstruct
public static Map<String, Map<Integer, String>> populateMasterList()
{
logger.debug("Calling Institute Info Masters");
List<InstituteInfoMaster> masterList = systemService.listInstituteInfoMasters();
Map<Integer, String> masterMap = new HashMap<Integer, String>();
masterMap.put(0, "---Select---");
masterList.forEach((master) ->
{
masterMap.put(master.getListId(), master.getValue());
});
logger.debug("Created Map for List Masters");
listMap.put("infoList", masterMap);
return listMap;
}
public Map<String, Map<Integer, String>> getListMap()
{
return listMap;
}
public static void setListMap()
{
listMap = populateMasterList();
}
#Override
public void afterPropertiesSet() throws Exception
{
populateMasterList();
}
}
I observed that it does not initializes on Application startup. when i try to update the master by calling DropDownList.setListMap(); it gives NullPointerException. but if i calls the jsp page where i am calling the Map as ${listService.listMap['infoList']} it displays the Drop-down on jsp after it if i tries to save master it executes successfully.
it means when i calls the jsp page where i am showing drop-down that time only it initializing the bean not on Application startup.
The actual problem is that you are not accessing the Spring bean, but the class, staticly. When you use the bean, i.e. the listService instance, Spring will initalize it for you on first access.
You are calling a static method, but when this happens, the dependant beans are not populated. Autowiring works for instances (i.e. in non-static context), so systemService is null in your application.
Update: I have just realized this line:
#Autowired
private static SystemService systemService;
This is fundamentally wrong. You cannot autowire static fields, it makes absolutely no sense in Spring (or in any similar framework). Spring beans are instances, and the framework set the autowired fields to references to other spring beans.
Based on parameters passed to a method, I need to select from one of many Spring beans that are implementations of the same class, but configured with different parameters.
E.g. if user A invokes the method, I need to call dooFoo() on bean A, but if it's user B then I need to call the very same method, only on bean B.
Is there a 'Springier' way of doing this other than sticking all the beans in a map, and deriving a key from the parameters passed to my method?
We face that issue in our project, and we solve it through a Factory-Like class. The client class -the one that needed the bean at runtime- had an instance of the factory, that was injected through Spring:
#Component
public class ImTheClient{
#Autowired
private ImTheFactory factory;
public void doSomething(
Parameters parameters) throws Exception{
IWantThis theInstance = factory.getInstance(parameters);
}
}
So, the IWantThis instance depends on the runtime value of the parameters parameter. The Factory implementation goes like this:
#Component
public class ImTheFactoryImpl implements
ImTheFactory {
#Autowired
private IWantThisBadly anInstance;
#Autowired
private IAlsoWantThis anotherInstance;
#Override
public IWantThis getInstance(Parameters parameters) {
if (parameters.equals(Parameters.THIS)) {
return anInstance;
}
if (parameters.equals(Parameters.THAT)) {
return anotherInstance;
}
return null;
}
}
So, the factory instance holds reference to both of the posible values of the IWantThis class, being IWantThisBadly and IAlsoWantThis both implementations of IWantThis.
Seems like do you want a ServiceLocator using the application context as registry.
See ServiceLocatorFactoryBean support class for creating ServiceLocators mapping keys to bean names without coupling client code to Spring.
Other option is to use a naming convention or annotation based configuration.
for example, assuming that you annotate Services with #ExampleAnnotation("someId"), you can use something like the following Service Locator to retrieve them.
public class AnnotationServiceLocator implements ServiceLocator {
#Autowired
private ApplicationContext context;
private Map<String, Service> services;
public Service getService(String id) {
checkServices();
return services.get(id);
}
private void checkServices() {
if (services == null) {
services = new HashMap<String, Service>();
Map<String, Object> beans = context.getBeansWithAnnotation(ExampleAnnotation.class);
for (Object bean : beans.values()) {
ExampleAnnotation ann = bean.getClass().getAnnotation(ExampleAnnotation.class);
services.put(ann.value(), (Service) bean);
}
}
}
}
Sticking them in a map sounds fine. If it's a Spring-managed map (using util:map, or in Java config), that's better than creating it somewhere else, because then Spring owns all the object references and can manage their lifecycle properly.
If the beans (A, B) you are talking about are SessionScope its no problem at all, they will be selected correctly.
public class BusinessLogic {
private BaseClassOfBeanAandB bean;
public void methodCalledByUserAorB() {
bean.doFoo();
}
}
I am using jboss 5.1.0.GA (build: SVNTag=JBoss_5_1_0_GA date=200905221634). And need to get business interface of the bean. That is necessary for transaction management.
So I have:
#Local
public interface MyBeanInterface {
void transactionalMethod();
}
#Stateless
public class MyBean implements MyBeanInterface {
#Resource
private SessionContext context;
private int aState;
public void someMethod() {
aState = 42;
context.getBusinessObject(MyBeanInterface.class).transactionalMethod();
}
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void transactionalMethod() {
System.out.println(aState); // 0!!!!!
}
}
For some reason I do not get the same bean, but new bean is created. That is disastrous as transactionalMethod needs the state variable value to execute correctly.
What am I doing wrong, or that is a bug of jboss? By the way there is a bug which affects ability to get business object via bean's class: https://issues.jboss.org/browse/EJBTHREE-2126. Not sure however if it relates to my issue.
The best solution is this:
#Stateless
public class MyBean implements MyBeanInterface {
#Resource private TransactionManager tm;
private int aState;
public void someMethod() {
aState = 42;
Transaction transaction = tm.suspend();
transactionalMethod();
tm.resume(transaction);
}
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void transactionalMethod() {
System.out.println(aState); // 0!!!!!
}
}
When you call a published interface method from the same istance, passing by ejb context, the resource is:
If it is #Stateless, a new instance is created.
If it is #Stateful, a new session is created for the first call, then other call are same as #Singleton.
If it is #Singleton, the caller waits for the resource to be freed, in case it calls itself, a deadlock is created. If the method is annotated with #Read, calling yourself does not create any deadlocks.
I don't have time to see if the syntax is perfect but you could try:
InitialContext jndiContext = new InitialContext();
Object ref = jndiContext.lookup("projname/MyBeanInterface/local");
MyBeanInterfaceLocal m = (MyBeanInterfaceLocal) ref;
However I saw that you have a SessionContext field, so maybe for you the code should be a little bit different. Maybe it would be:
Object ref = SessionContext.lookup("projname/MyBeanInterface/local");
MyBeanInterfaceLocal m = (MyBeanInterfaceLocal) ref;
Let me know if this helps!