package org.jboss.modules;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;

/**
 * 
 * @author <a href="mailto:david.lloyd@redhat.com">David M. Lloyd</a>
 * @author <a href="mailto:jbailey@redhat.com">John Bailey</a>
 */
public abstract class ModuleLoader {

    private static ThreadLocal<Deque<ModuleIdentifier>> LOAD_CALL_STACK_HOLDER = new ThreadLocal<Deque<ModuleIdentifier>>() {
        @Override
        protected Deque<ModuleIdentifier> initialValue() {
            return new ArrayDeque<ModuleIdentifier>();
        }
    };

    private final HashMap<ModuleIdentifier, Module> moduleMap = new HashMap<ModuleIdentifier, Module>();

    /**
     * Load a module based on an identifier.
     *
     * @param identifier The module identifier
     * @return The loaded Module
     * @throws ModuleLoadException if the Module can not be loaded
     */
    public Module loadModule(ModuleIdentifier identifier) throws ModuleLoadException {
        final Deque<ModuleIdentifier> callStack = LOAD_CALL_STACK_HOLDER.get();

        if(callStack.contains(identifier))
            throw new ModuleLoadException("Module cycle discovered: " + callStack);

        callStack.push(identifier);
        try {
            final Module module = findModule(identifier);
            if (module == null) {
                throw new ModuleNotFoundException(identifier.toString());
            }
            return module;
        } finally {
            callStack.pop();
        }
    }

    /**
     * Find a Module by its identifier.  This should be overriden by sub-classes
     * to provide custom Module loading strategies.  Implementations of this method
     * should call {@link #defineModule}
     *
     * @param moduleIdentifier The modules Identifier
     * @return The Module
     * @throws ModuleLoadException If any problems occur finding the module
     */
    protected abstract Module findModule(final ModuleIdentifier moduleIdentifier) throws ModuleLoadException;

    /**
     * Defines a Module based on a specification.  Use of this method is required by
     * any ModuleLoader implementations in order to fully define a Module. 
     *
     * @param moduleSpec The module specification to create the Module from
     * @return The defined Module
     * @throws ModuleLoadException If any dependent modules can not be loaded
     */
    protected final Module defineModule(ModuleSpec moduleSpec) throws ModuleLoadException {

        final ModuleIdentifier moduleIdentifier = moduleSpec.getIdentifier();

        synchronized (moduleMap) {
            final Module oldModule = moduleMap.get(moduleIdentifier);
            if (oldModule != null) {
                throw new ModuleAlreadyExistsException(moduleIdentifier.toString());
            }
            final List<Dependency> dependencies = new ArrayList<Dependency>(moduleSpec.getDependencies().size());
            for (DependencySpec dependencySpec : moduleSpec.getDependencies()) {
                final Dependency dependency = new Dependency(loadModule(dependencySpec.getModuleIdentifier()), dependencySpec.isExport());
                dependencies.add(dependency);
            }

            final Module module = new Module(moduleSpec, dependencies, moduleSpec.getModuleFlags(), this);
            moduleMap.put(moduleIdentifier, module);
            return module;
        }
    }
}

@@ -25,37 +25,8 @@
 s;%0A%0A
-import java.util.ArrayDeque;%0A
 impo
@@ -66,21 +66,23 @@
 va.util.
-Deque
+HashMap
 ;%0Aimport
@@ -88,35 +88,35 @@
 t java.util.Hash
-Map
+Set
 ;%0Aimport java.ut
@@ -123,16 +123,38 @@
 il.List;
+%0Aimport java.util.Set;
 %0A%0A/**%0A *
@@ -353,37 +353,35 @@
 tic ThreadLocal%3C
-Deque
+Set
 %3CModuleIdentifie
@@ -384,38 +384,23 @@
 ifier%3E%3E 
-LOAD_CALL_STACK_HOLDER
+VISITED
  = new T
@@ -410,21 +410,19 @@
 adLocal%3C
-Deque
+Set
 %3CModuleI
@@ -473,21 +473,19 @@
 otected 
-Deque
+Set
 %3CModuleI
@@ -539,18 +539,15 @@
 new 
-ArrayDeque
+HashSet
 %3CMod
@@ -1008,13 +1008,11 @@
 nal 
-Deque
+Set
 %3CMod
@@ -1030,42 +1030,25 @@
 er%3E 
-callStack = LOAD_CALL_STACK_HOLDER
+visited = VISITED
 .get
@@ -1063,25 +1063,23 @@
      if(
-callStack
+visited
 .contain
@@ -1165,25 +1165,23 @@
 ed: %22 + 
-callStack
+visited
 );%0A%0A    
@@ -1188,22 +1188,205 @@
     
-callStack.push
+synchronized (moduleMap) %7B%0A            final Module module = moduleMap.get(identifier);%0A            if (module != null) %7B%0A                return module;%0A            %7D%0A        %7D%0A%0A        visited.add
 (ide
@@ -1652,22 +1652,33 @@
     
-callStack.pop(
+visited.remove(identifier
 );%0A 

Fix cycle detection, fix redefinition protection