/*
 * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */

package org.opendaylight.controller.config.spi;

import org.opendaylight.controller.config.api.DependencyResolver;
import org.opendaylight.controller.config.api.ModuleIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Base implementation of Module. This implementation contains base logic for Module reconfiguration with associated fields.
 * @param <M> Type of module implementation. Enables easier implementation for the {@link #isSame} method
 */
public abstract class AbstractModule<M extends AbstractModule<M>> implements org.opendaylight.controller.config.spi.Module {

    private static final Logger LOG = LoggerFactory.getLogger(AbstractModule.class);

    private final M oldModule;
    private final AutoCloseable oldInstance;
    protected final ModuleIdentifier identifier;
    private AutoCloseable instance;
    protected final DependencyResolver dependencyResolver;

    /**
     * Called when module is configured.
     *
     * @param identifier id of current instance.
     * @param dependencyResolver resolver used in dependency injection and validation.
     */
    public AbstractModule(ModuleIdentifier identifier, DependencyResolver dependencyResolver) {
        this(identifier, dependencyResolver, null, null);
    }

    /**
     * Called when module is reconfigured.
     *
     * @param identifier id of current instance.
     * @param dependencyResolver resolver used in dependency injection and validation.
     * @param oldModule old instance of module that is being reconfigred(replaced) by current instance. The old instance can be examined for reuse.
     * @param oldInstance old instance wrapped by the old module. This is the resource that is actually being reused if possible or closed otherwise.
     */
    public AbstractModule(ModuleIdentifier identifier, DependencyResolver dependencyResolver, M oldModule, AutoCloseable oldInstance) {
        this.identifier = identifier;
        this.dependencyResolver = dependencyResolver;
        this.oldModule = oldModule;
        this.oldInstance = oldInstance;
    }

    @Override
    public ModuleIdentifier getIdentifier() {
        return identifier;
    }

    /**
     *
     * General algorithm for spawning/closing and reusing wrapped instances.
     *
     * @return current instance of wrapped resource either by reusing the old one (if present) or constructing a brand new.
     */
    @Override
    public final AutoCloseable getInstance() {
        if(instance==null) {
            if(oldInstance!=null && canReuseInstance(oldModule)) {
                resolveDependencies();
                instance = reuseInstance(oldInstance);
            } else {
                if(oldInstance!=null) {
                    try {
                        oldInstance.close();
                    } catch(Exception e) {
                        LOG.error("An error occurred while closing old instance {} for module {}", oldInstance, getIdentifier(), e);
                    }
                }
                resolveDependencies();
                instance = createInstance();
                if (instance == null) {
                    throw new IllegalStateException("Error in createInstance - null is not allowed as return value. Module: " + getIdentifier());
                }
            }
        }
        return instance;
    }

    /**
     * @return Brand new instance of wrapped class in case no previous instance is present or reconfiguration is impossible.
     */
    protected abstract AutoCloseable createInstance();

    @Override
    public final boolean canReuse(Module oldModule) {
        // Just cast into a specific instance
        // TODO unify this method with canReuseInstance (required Module interface to be generic which requires quite a lot of changes)
        return getClass().isInstance(oldModule) ? canReuseInstance((M) oldModule) : false;
    }

    /**
     *
     * Users are welcome to override this method to provide custom logic for advanced reusability detection.
     *
     * @param oldModule old instance of a Module
     * @return true if the old instance is reusable false if a new one should be spawned
     */
    protected abstract boolean canReuseInstance(final M oldModule);

    /**
     * By default the oldInstance is returned since this method is by default called only if the oldModule had the same configuration and dependencies configured.
     * Users are welcome to override this method to provide custom logic for advanced reusability.
     *
     * @param oldInstance old instance of a class wrapped by the module
     * @return reused instance
     */
    protected AutoCloseable reuseInstance(AutoCloseable oldInstance) {
        // implement if instance reuse should be supported. Override canReuseInstance to change the criteria.
        return oldInstance;
    }

    /**
     * Inject all the dependencies using dependency resolver instance.
     */
    protected abstract void resolveDependencies();
}

@@ -1000,76 +1000,59 @@
   pr
-ivate final M oldModule;%0A    private final AutoCloseable oldInstance
+otected final DependencyResolver dependencyResolver
 ;%0A  
@@ -1094,24 +1094,25 @@
 identifier;%0A
+%0A
     private 
@@ -1125,17 +1125,20 @@
 oseable 
-i
+oldI
 nstance;
@@ -1148,59 +1148,61 @@
   pr
-otected final DependencyResolver dependencyResolver
+ivate M oldModule;%0A    private AutoCloseable instance
 ;%0A%0A 
@@ -1418,32 +1418,38 @@
  AbstractModule(
+final 
 ModuleIdentifier
@@ -1452,32 +1452,38 @@
 fier identifier,
+ final
  DependencyResol
@@ -2099,16 +2099,22 @@
 tModule(
+final 
 ModuleId
@@ -2133,16 +2133,22 @@
 ntifier,
+ final
  Depende
@@ -2178,16 +2178,22 @@
 esolver,
+ final
  M oldMo
@@ -2197,16 +2197,22 @@
 dModule,
+ final
  AutoClo
@@ -2804,16 +2804,17 @@
       if
+ 
 (instanc
@@ -2814,18 +2814,20 @@
 instance
+ 
 ==
+ 
 null) %7B%0A
@@ -2832,32 +2832,33 @@
 %7B%0A            if
+ 
 (oldInstance!=nu
@@ -2849,26 +2849,28 @@
 (oldInstance
+ 
 !=
+ 
 null && canR
@@ -3029,16 +3029,17 @@
       if
+ 
 (oldInst
@@ -3042,18 +3042,20 @@
 Instance
+ 
 !=
+ 
 null) %7B%0A
@@ -3152,16 +3152,17 @@
  %7D catch
+ 
 (Excepti
@@ -3635,32 +3635,192 @@
  %7D%0A            %7D
+%0A%0A            // Prevent serial memory leak: clear these references as we will not use them again.%0A            oldInstance = null;%0A            oldModule = null;
 %0A        %7D%0A     
@@ -3806,32 +3806,33 @@
 null;%0A        %7D%0A
+%0A
         return i
@@ -4092,16 +4092,22 @@
 anReuse(
+final 
 Module o
@@ -5174,16 +5174,22 @@
 nstance(
+final 
 AutoClos

clear oldModule/oldInstance when no longer needed