BUG-4514: clear oldModule/oldInstance when no longer needed
[controller.git] / opendaylight / config / config-api / src / main / java / org / opendaylight / controller / config / spi / AbstractModule.java
1 package org.opendaylight.controller.config.spi;
2
3 import org.opendaylight.controller.config.api.DependencyResolver;
4 import org.opendaylight.controller.config.api.ModuleIdentifier;
5 import org.slf4j.Logger;
6 import org.slf4j.LoggerFactory;
7
8 /**
9  * Base implementation of Module. This implementation contains base logic for Module reconfiguration with associated fields.
10  * @param <M> Type of module implementation. Enables easier implementation for the {@link #isSame} method
11  */
12 public abstract class AbstractModule<M extends AbstractModule<M>> implements org.opendaylight.controller.config.spi.Module {
13
14     private static final Logger LOG = LoggerFactory.getLogger(AbstractModule.class);
15
16     protected final DependencyResolver dependencyResolver;
17     protected final ModuleIdentifier identifier;
18
19     private AutoCloseable oldInstance;
20     private M oldModule;
21     private AutoCloseable instance;
22
23     /**
24      * Called when module is configured.
25      *
26      * @param identifier id of current instance.
27      * @param dependencyResolver resolver used in dependency injection and validation.
28      */
29     public AbstractModule(final ModuleIdentifier identifier, final DependencyResolver dependencyResolver) {
30         this(identifier, dependencyResolver, null, null);
31     }
32
33     /**
34      * Called when module is reconfigured.
35      *
36      * @param identifier id of current instance.
37      * @param dependencyResolver resolver used in dependency injection and validation.
38      * @param oldModule old instance of module that is being reconfigred(replaced) by current instance. The old instance can be examined for reuse.
39      * @param oldInstance old instance wrapped by the old module. This is the resource that is actually being reused if possible or closed otherwise.
40      */
41     public AbstractModule(final ModuleIdentifier identifier, final DependencyResolver dependencyResolver, final M oldModule, final AutoCloseable oldInstance) {
42         this.identifier = identifier;
43         this.dependencyResolver = dependencyResolver;
44         this.oldModule = oldModule;
45         this.oldInstance = oldInstance;
46     }
47
48     @Override
49     public ModuleIdentifier getIdentifier() {
50         return identifier;
51     }
52
53     /**
54      *
55      * General algorithm for spawning/closing and reusing wrapped instances.
56      *
57      * @return current instance of wrapped resource either by reusing the old one (if present) or constructing a brand new.
58      */
59     @Override
60     public final AutoCloseable getInstance() {
61         if (instance == null) {
62             if (oldInstance != null && canReuseInstance(oldModule)) {
63                 resolveDependencies();
64                 instance = reuseInstance(oldInstance);
65             } else {
66                 if (oldInstance != null) {
67                     try {
68                         oldInstance.close();
69                     } catch (Exception e) {
70                         LOG.error("An error occurred while closing old instance {} for module {}", oldInstance, getIdentifier(), e);
71                     }
72                 }
73                 resolveDependencies();
74                 instance = createInstance();
75                 if (instance == null) {
76                     throw new IllegalStateException("Error in createInstance - null is not allowed as return value. Module: " + getIdentifier());
77                 }
78             }
79
80             // Prevent serial memory leak: clear these references as we will not use them again.
81             oldInstance = null;
82             oldModule = null;
83         }
84
85         return instance;
86     }
87
88     /**
89      * @return Brand new instance of wrapped class in case no previous instance is present or reconfiguration is impossible.
90      */
91     protected abstract AutoCloseable createInstance();
92
93     @Override
94     public final boolean canReuse(final Module oldModule) {
95         // Just cast into a specific instance
96         // TODO unify this method with canReuseInstance (required Module interface to be generic which requires quite a lot of changes)
97         return getClass().isInstance(oldModule) ? canReuseInstance((M) oldModule) : false;
98     }
99
100     /**
101      *
102      * Users are welcome to override this method to provide custom logic for advanced reusability detection.
103      *
104      * @param oldModule old instance of a Module
105      * @return true if the old instance is reusable false if a new one should be spawned
106      */
107     protected abstract boolean canReuseInstance(final M oldModule);
108
109     /**
110      * By default the oldInstance is returned since this method is by default called only if the oldModule had the same configuration and dependencies configured.
111      * Users are welcome to override this method to provide custom logic for advanced reusability.
112      *
113      * @param oldInstance old instance of a class wrapped by the module
114      * @return reused instance
115      */
116     protected AutoCloseable reuseInstance(final AutoCloseable oldInstance) {
117         // implement if instance reuse should be supported. Override canReuseInstance to change the criteria.
118         return oldInstance;
119     }
120
121     /**
122      * Inject all the dependencies using dependency resolver instance.
123      */
124     protected abstract void resolveDependencies();
125 }