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