Don Willis avatar Don Willis committed fe853f2

SOY-5 Dropped scoped functions because they didn't work

- Deleted the function finding functionality from ModuleDataFactory and renamed it TemplateSetFactory. Also made it deal only with a single module key instead of a set.
- Deleted the scoped injectors from the SoyDependencyInjectorFactory.

Comments (0)

Files changed (8)

soy-template-plugin/src/main/java/com/atlassian/soy/impl/DefaultSoyManager.java

 import com.google.common.base.Function;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.MapMaker;
-import com.google.inject.*;
+import com.google.inject.AbstractModule;
+import com.google.inject.Injector;
+import com.google.inject.Module;
 import com.google.inject.multibindings.Multibinder;
 import com.google.inject.util.Modules;
 import com.google.template.soy.SoyFileSet;
 import com.google.template.soy.shared.restricted.SoyFunction;
 import com.google.template.soy.tofu.SoyTofu;
 import com.google.template.soy.xliffmsgplugin.XliffMsgPluginModule;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
 import java.net.URL;
-import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.TimeUnit;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 
 public class DefaultSoyManager implements SoyManager
 {
      * Compiled Soy templates keyed on complete plugin-module key.
      */
     private ConcurrentMap<String, SoyTofu> soyTofuCache;
-    private final ModuleDataFactory moduleDataFactory;
+    private final TemplateSetFactory templateSetFactory;
     private final SoyDependencyInjectorFactory soyDependencyInjectorFactory;
 
     public DefaultSoyManager(PluginAccessor pluginAccessor, PluginEventManager pluginEventManager,
             }
         });
 
-        soyDependencyInjectorFactory = new SoyDependencyInjectorFactory(defaultModules, soyDataConverter, INJECTOR_CACHE_EXPIRY_TIME, INJECTOR_CACHE_EXPIRY_TIME_UNIT);
-        moduleDataFactory = new ModuleDataFactory(pluginAccessor, servletContextFactory, INJECTOR_CACHE_EXPIRY_TIME, INJECTOR_CACHE_EXPIRY_TIME_UNIT);
+        soyDependencyInjectorFactory = new SoyDependencyInjectorFactory(defaultModules);
+        templateSetFactory = new TemplateSetFactory(pluginAccessor, servletContextFactory, INJECTOR_CACHE_EXPIRY_TIME, INJECTOR_CACHE_EXPIRY_TIME_UNIT);
         pluginEventManager.register(this);
     }
 
     {
         soyTofuCache.clear();
         soyDependencyInjectorFactory.clear();
-        moduleDataFactory.clear();
+        templateSetFactory.clear();
     }
 
     /*
      */
     private SoyTofu strainTofu(String completeModuleKey)
     {
-        return makeBuilder(completeModuleKey).build().compileToJavaObj();
-    }
+        SoyFileSet.Builder builder = makeSoyFileSetBuilder();
 
-    public SoyFileSet.Builder makeBuilder(String... functionModuleKeys)
-    {
-        List<String> keyList = ImmutableList.copyOf(functionModuleKeys);
-        ModuleData data = moduleDataFactory.get(keyList);
-        Injector injector = soyDependencyInjectorFactory.get(data);
-        SoyFileSet.Builder builder = injector.getInstance(SoyFileSet.Builder.class);
-        for (URL file : data.getFileSet())
+        for (URL file : templateSetFactory.get(completeModuleKey))
         {
             builder.add(file);
         }
 
-        return builder;
+        return builder.build().compileToJavaObj();
+    }
+
+    public SoyFileSet.Builder makeSoyFileSetBuilder()
+    {
+        Injector injector = soyDependencyInjectorFactory.get();
+        return injector.getInstance(SoyFileSet.Builder.class);
     }
 
     private boolean isDevMode()
                     .toInstance(apiCallScope);
         }
     }
-
 }

soy-template-plugin/src/main/java/com/atlassian/soy/impl/ModuleData.java

-package com.atlassian.soy.impl;
-
-import com.atlassian.soy.renderer.SoyServerFunction;
-import com.google.common.collect.ImmutableSet;
-import com.google.template.soy.shared.restricted.SoyFunction;
-
-import java.net.URL;
-import java.util.Set;
-
-/**
- * Cacheable collection of data associated with a set of resource module keys.
- */
-final class ModuleData
-{
-    private final Set<SoyServerFunction<?>> wrappedFunctions;
-    private final Set<SoyFunction> soyFunctions;
-    private final Set<URL> fileSet;
-
-    public ModuleData(Set<SoyServerFunction<?>> wrappedFunctions, Set<SoyFunction> soyFunctions,
-                      Set<URL> fileSet)
-    {
-        this.wrappedFunctions = ImmutableSet.copyOf(wrappedFunctions);
-        this.soyFunctions = ImmutableSet.copyOf(soyFunctions);
-        this.fileSet = ImmutableSet.copyOf(fileSet);
-    }
-
-    public Set<SoyServerFunction<?>> getWrappedFunctions()
-    {
-        return wrappedFunctions;
-    }
-
-    public Set<SoyFunction> getSoyFunctions()
-    {
-        return soyFunctions;
-    }
-
-    /**
-     * Soy template files accessible through the modules
-     */
-    public Set<URL> getFileSet()
-    {
-        return fileSet;
-    }
-
-    @Override
-    public String toString()
-    {
-        return "FunctionModuleData{" +
-            "wrappedFunctions=" + wrappedFunctions +
-            ", soyFunctions=" + soyFunctions +
-            ", fileSet=" + fileSet +
-            '}';
-    }
-
-    @Override
-    public boolean equals(Object o)
-    {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        ModuleData that = (ModuleData) o;
-
-        if (!fileSet.equals(that.fileSet)) return false;
-        if (!soyFunctions.equals(that.soyFunctions)) return false;
-        if (!wrappedFunctions.equals(that.wrappedFunctions)) return false;
-
-        return true;
-    }
-
-    @Override
-    public int hashCode()
-    {
-        int result = wrappedFunctions.hashCode();
-        result = 31 * result + soyFunctions.hashCode();
-        result = 31 * result + fileSet.hashCode();
-        return result;
-    }
-}

soy-template-plugin/src/main/java/com/atlassian/soy/impl/ModuleDataFactory.java

-package com.atlassian.soy.impl;
-
-import com.atlassian.plugin.ModuleDescriptor;
-import com.atlassian.plugin.PluginAccessor;
-import com.atlassian.plugin.elements.ResourceDescriptor;
-import com.atlassian.plugin.servlet.ServletContextFactory;
-import com.atlassian.plugin.webresource.WebResourceModuleDescriptor;
-import com.atlassian.soy.renderer.SoyResourceModuleDescriptor;
-import com.atlassian.soy.renderer.SoyServerFunction;
-import com.google.common.base.Function;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.MapMaker;
-import com.google.common.collect.Sets;
-import com.google.template.soy.shared.restricted.SoyFunction;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Determines and caches the appropriate {@link ModuleData} for different sets of plugin modules.
- */
-class ModuleDataFactory
-{
-    private static final Logger log = LoggerFactory.getLogger(ModuleDataFactory.class);
-
-    private final PluginAccessor pluginAccessor;
-    private final ServletContextFactory servletContextFactory;
-
-    /**
-     * Requisite config so we can make an Injector and a Builder without parsing all the modules twice.
-     */
-    private final ConcurrentMap<List<String>, ModuleData> moduleDataCache;
-
-    ModuleDataFactory(PluginAccessor pluginAccessor, ServletContextFactory servletContextFactory,
-                      int injectorCacheExpiryTime, TimeUnit injectorCacheExpiryTimeUnit)
-    {
-        this.pluginAccessor = pluginAccessor;
-        this.servletContextFactory = servletContextFactory;
-        Function<List<String>, ModuleData> dataProducer = new Function<List<String>, ModuleData>()
-        {
-            @Override
-            public ModuleData apply(List<String> moduleKeys)
-            {
-                return extractModuleData(moduleKeys);
-            }
-        };
-
-        moduleDataCache = new MapMaker()
-            .expiration(injectorCacheExpiryTime, injectorCacheExpiryTimeUnit)
-            .makeComputingMap(dataProducer);
-
-    }
-
-    /**
-     * Retrieve the {@link ModuleData} applicable to a given set of plugin module keys.  ModuleData includes template
-     * files and function sets. Functions can be extracted from the SoyFunctions _within_ SoyWebResources and from
-     * SoyWebResources and WebResources that _depend_ on those SoyWebResources. Templates can be extracted from
-     * WebResources and SoyWebResources in the dependency trees of any of the given plugin keys.
-     */
-    ModuleData get(Iterable<String> pluginModulesKeys)
-    {
-        return moduleDataCache.get(ImmutableList.copyOf(pluginModulesKeys));
-    }
-
-    void clear()
-    {
-        moduleDataCache.clear();
-    }
-
-
-    private ModuleData extractModuleData(Iterable<String> pluginModuleKeys)
-    {
-        log.debug("Extracting Soy Module Data for '{}'", pluginModuleKeys);
-        ModuleDataBuilder moduleDataBuilder = new ModuleDataBuilder();
-
-        for (String pluginModuleKey : pluginModuleKeys)
-        {
-            moduleDataBuilder.addTemplatesAndFunctionsForTree(pluginModuleKey);
-        }
-        ModuleData result = moduleDataBuilder.build();
-        log.debug("Extracted Soy Module Data for '{}' was {}", pluginModuleKeys, result);
-        return result;
-    }
-
-    private class ModuleDataBuilder
-    {
-        private final Set<SoyServerFunction<?>> wrappedFunctions = Sets.newHashSet();
-        private final Set<SoyFunction> soyFunctions = Sets.newHashSet();
-        private final Set<URL> fileSet = Sets.newHashSet();
-        private final Set<String> alreadyAddedModules = Sets.newHashSet();
-
-        private void addTemplatesAndFunctionsForTree(String completeModuleKey)
-        {
-            if (alreadyAddedModules.contains(completeModuleKey))
-                return;
-
-            ModuleDescriptor<?> moduleDescriptor = pluginAccessor.getEnabledPluginModule(completeModuleKey);
-
-            if (moduleDescriptor == null)
-                throw new IllegalStateException("Required plugin module " + completeModuleKey + " was either missing or disabled");
-
-            if (moduleDescriptor instanceof WebResourceModuleDescriptor)
-                addTemplatesAndFunctionsForTree((WebResourceModuleDescriptor) moduleDescriptor);
-
-            addSoyTemplateResources(moduleDescriptor);
-
-            if (moduleDescriptor instanceof SoyResourceModuleDescriptor)
-                addFunctionsFromSoyModuleDescriptor((SoyResourceModuleDescriptor) moduleDescriptor);
-
-            alreadyAddedModules.add(completeModuleKey);
-        }
-
-        private void addTemplatesAndFunctionsForTree(WebResourceModuleDescriptor webResourceModuleDescriptor)
-        {
-            for (String dependencyModuleKey : webResourceModuleDescriptor.getDependencies())
-            {
-                addTemplatesAndFunctionsForTree(dependencyModuleKey);
-            }
-        }
-
-        private void addFunctionsFromSoyModuleDescriptor(SoyResourceModuleDescriptor soyModuleDescriptor)
-        {
-
-            for (SoyServerFunction<?> function : soyModuleDescriptor.getFunctions())
-            {
-                wrappedFunctions.add(function);
-            }
-
-            for (Object nativeFunction : soyModuleDescriptor.getNativeFunctions())
-            {
-                if (nativeFunction instanceof SoyFunction)
-                    soyFunctions.add((SoyFunction) nativeFunction);
-                else
-                    log.error("Could not load Soy function {} because it is not a compatible class", nativeFunction);
-            }
-        }
-
-        private void addSoyTemplateResources(ModuleDescriptor<?> moduleDescriptor)
-        {
-            for (ResourceDescriptor resource : moduleDescriptor.getResourceDescriptors())
-            {
-                if (isSoyTemplate(resource))
-                {
-                    URL url = getSoyResourceURL(moduleDescriptor, resource);
-                    if (url != null)
-                    {
-                        this.fileSet.add(url);
-                    }
-                }
-            }
-        }
-
-        private boolean isSoyTemplate(ResourceDescriptor resource)
-        {
-            return resource.getLocation().endsWith(".soy");
-        }
-
-        private URL getSoyResourceURL(ModuleDescriptor moduleDescriptor, ResourceDescriptor resource)
-        {
-            final String sourceParam = resource.getParameter("source");
-            if ("webContextStatic".equalsIgnoreCase(sourceParam))
-            {
-                try
-                {
-                    return servletContextFactory.getServletContext().getResource(resource.getLocation());
-                }
-                catch (MalformedURLException e)
-                {
-                    log.error("Ignoring soy resource. Could not locate soy with location: " + resource.getLocation());
-                    return null;
-                }
-            }
-            return moduleDescriptor.getPlugin().getResource(resource.getLocation());
-        }
-
-        private ModuleData build()
-        {
-            return new ModuleData(wrappedFunctions, soyFunctions, fileSet);
-        }
-    }
-}

soy-template-plugin/src/main/java/com/atlassian/soy/impl/SoyDependencyInjectorFactory.java

 package com.atlassian.soy.impl;
 
-import com.atlassian.soy.renderer.SoyServerFunction;
 import com.atlassian.util.concurrent.ResettableLazyReference;
-import com.google.common.base.Function;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.MapMaker;
-import com.google.inject.AbstractModule;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Module;
-import com.google.inject.multibindings.Multibinder;
-import com.google.template.soy.shared.restricted.SoyFunction;
-
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.TimeUnit;
 
 /**
  * Creates and Caches Injectors for different sets of additional SoyFunctions that might be required by different
  */
 class SoyDependencyInjectorFactory
 {
-    /** Reset the defaultInjectorRef on plugin enabling / disabling as it includes plugin defined global functions */
-    private final ResettableLazyReference<Injector> defaultInjectorRef;
-    private final Iterable<Module> defaultModules;
-
     /**
-     * Injectors are relatively expensive and synchronous to create. Don't do it unless you have to.
+     * Injectors are relatively expensive and synchronous to create. Don't do it unless you have to. It needs to be
+     * reset on plugin enabling / disabling as it includes plugin defined global functions.
      */
-    private final ConcurrentMap<ModuleData, Injector> injectorCache;
-    private SoyDataConverter soyDataConverter;
+    private final ResettableLazyReference<Injector> defaultInjectorRef;
+    private final Iterable<Module> defaultModules;
 
-    public SoyDependencyInjectorFactory(Iterable<Module> defaultModules, SoyDataConverter soyDataConverter, int injectorCacheExpiryTime, TimeUnit injectorCacheExpiryTimeUnit)
+    public SoyDependencyInjectorFactory(Iterable<Module> defaultModules)
     {
         this.defaultModules = defaultModules;
-        this.soyDataConverter = soyDataConverter;
-        this.injectorCache = new MapMaker().expiration(injectorCacheExpiryTime, injectorCacheExpiryTimeUnit).makeComputingMap(
-                new Function<ModuleData, Injector>()
-        {
-            @Override
-            public Injector apply(ModuleData functionModuleData)
-            {
-                return makeInjectorForKeys(functionModuleData, SoyDependencyInjectorFactory.this.soyDataConverter);
-            }
-        });
-
         this.defaultInjectorRef = new ResettableLazyReference<Injector>() {
             @Override
             protected Injector create() throws Exception
             {
-                return createInjectorForModules();
+                return Guice.createInjector(SoyDependencyInjectorFactory.this.defaultModules);
             }
         };
-
-    }
-
-    private Injector makeInjectorForKeys(ModuleData data, SoyDataConverter soyDataConverter)
-    {
-        Injector defaultInjector = defaultInjectorRef.get();
-        if (data.getSoyFunctions().isEmpty() && data.getWrappedFunctions().isEmpty())
-        {
-            return defaultInjector;
-        }
-
-        return createInjectorForModules(new FunctionsModule(soyDataConverter, data.getWrappedFunctions(), data.getSoyFunctions()));
-    }
-
-    private Injector createInjectorForModules(Module... additionalModules)
-    {
-        ImmutableList.Builder<Module> builder = ImmutableList.builder();
-        ImmutableList<Module> modules = builder.addAll(defaultModules).add(additionalModules).build();
-        return Guice.createInjector(modules);
     }
 
     public void clear()
     {
         defaultInjectorRef.reset();
-        injectorCache.clear();
     }
 
     /**
-     * Gets an injector initialised with custom functions according to {@link ModuleData#getSoyFunctions()} and
-     * {@link ModuleData#getWrappedFunctions()}. <b>WARNING:</b> Counter-intuitively, the available functions are _not_
-     * effectively tied to the injector, but to the JVM.  So initialising new injectors changes the available functions
-     * to all injectors.  See SOY-5.
+     * Gets the Injector for creating Soy objects.
      */
-    public Injector get(ModuleData data)
-    {
-        return injectorCache.get(data);
-    }
-
-    private static class FunctionsModule extends AbstractModule
+    public Injector get()
     {
-        private final SoyDataConverter soyDataConverter;
-        private final Iterable<SoyServerFunction<?>> functions;
-        private final Iterable<SoyFunction> soyFunctions;
-
-        public FunctionsModule(SoyDataConverter soyDataConverter, Iterable<SoyServerFunction<?>> wrappedFunctions, Iterable<SoyFunction> soyFunctions) {
-            this.soyDataConverter = soyDataConverter;
-            this.functions = wrappedFunctions;
-            this.soyFunctions = soyFunctions;
-        }
-
-        @Override
-        protected void configure()
-        {
-            Multibinder<SoyFunction> binder = Multibinder.newSetBinder(binder(), SoyFunction.class);
-
-            for (SoyServerFunction function : functions)
-                binder.addBinding().toInstance(new SoyTofuFunctionAdapter(function, soyDataConverter));
-
-            for (SoyFunction soyFunction : soyFunctions)
-                binder.addBinding().toInstance(soyFunction);
-        }
+        return defaultInjectorRef.get();
     }
 }

soy-template-plugin/src/main/java/com/atlassian/soy/impl/SoyManager.java

 package com.atlassian.soy.impl;
 
 import com.atlassian.soy.renderer.SoyException;
-import com.google.inject.Module;
 import com.google.template.soy.SoyFileSet;
 
 import java.util.Map;
 public interface SoyManager
 {
     /**
-     * Make a SoyFileSet builder, loading soy functions and templates from the provided modules. Modules may be
-     * soy-resource modules (loading functions and templates directly) or web-resource modules (loading any
-     * functions or templates referenced by one of the modules dependencies)
-     *
-     * @param functionModuleKeys the plugin keys of any module that can provide soy functions or templates
-     * @return a new Soy builder instance for that module
+     * Make a new SoyFileSet builder.
      */
-    public SoyFileSet.Builder makeBuilder(String... functionModuleKeys);
+    public SoyFileSet.Builder makeSoyFileSetBuilder();
 
     /**
      * Render a template from a module key into an appendable.

soy-template-plugin/src/main/java/com/atlassian/soy/impl/SoyTransformer.java

 import com.google.template.soy.jssrc.SoyJsSrcOptions;
 import org.dom4j.Element;
 
-import java.util.ArrayList;
 import java.util.List;
 
 /**
     @Override
     public DownloadableResource transform(Element element, ResourceLocation location, String filePath, DownloadableResource nextResource)
     {
-        return new SoyResource(getFunctionModules(element), nextResource, location.getLocation());
-    }
-
-    private String[] getFunctionModules(Element element)
-    {
-        List<String> functionModules = new ArrayList<String>();
-
-        for (Object functionElement : element.elements("functions"))
-            functionModules.add(((Element)functionElement).getTextTrim());
-
-        return functionModules.toArray(new String[functionModules.size()]);
+        return new SoyResource(nextResource, location.getLocation());
     }
 
     private class SoyResource extends CharSequenceDownloadableResource
     {
-        private final String[] pluginKeys;
         private final String location;
         private final SoyJsSrcOptions jsSrcOptions;
 
-        private SoyResource(String[] pluginKeys, DownloadableResource originalResource, String location)
+        private SoyResource(DownloadableResource originalResource, String location)
         {
             super(originalResource);
-            this.pluginKeys = pluginKeys;
             this.location = location;
             this.jsSrcOptions = new SoyJsSrcOptions();
             this.jsSrcOptions.setShouldGenerateJsdoc(false);
         @Override
         protected CharSequence transform(CharSequence originalContent)
         {
-            SoyFileSet.Builder sfsBuilder = soyManager.makeBuilder(pluginKeys);
+            SoyFileSet.Builder sfsBuilder = soyManager.makeSoyFileSetBuilder();
             sfsBuilder.add(originalContent, location);
 
             SoyFileSet sfs = sfsBuilder.build();

soy-template-plugin/src/main/java/com/atlassian/soy/impl/TemplateSetFactory.java

+package com.atlassian.soy.impl;
+
+import com.atlassian.plugin.ModuleDescriptor;
+import com.atlassian.plugin.PluginAccessor;
+import com.atlassian.plugin.elements.ResourceDescriptor;
+import com.atlassian.plugin.servlet.ServletContextFactory;
+import com.atlassian.plugin.webresource.WebResourceModuleDescriptor;
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.MapMaker;
+import com.google.common.collect.Sets;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Determines and caches the appropriate set of template file URLs for different sets of plugin modules.
+ */
+class TemplateSetFactory
+{
+    private static final Logger log = LoggerFactory.getLogger(TemplateSetFactory.class);
+
+    private final PluginAccessor pluginAccessor;
+    private final ServletContextFactory servletContextFactory;
+
+    /**
+     * Requisite config so we can make an Injector and a Builder without parsing all the modules twice.
+     */
+    private final ConcurrentMap<String, Set<URL>> templateSetCache;
+
+    TemplateSetFactory(PluginAccessor pluginAccessor, ServletContextFactory servletContextFactory,
+                       int injectorCacheExpiryTime, TimeUnit injectorCacheExpiryTimeUnit)
+    {
+        this.pluginAccessor = pluginAccessor;
+        this.servletContextFactory = servletContextFactory;
+        Function<String, Set<URL>> findTemplatesFunction = new Function<String, Set<URL>>()
+        {
+            @Override
+            public Set<URL> apply(String moduleKey)
+            {
+                return findRequiredTemplates(moduleKey);
+            }
+        };
+
+        templateSetCache = new MapMaker()
+            .expiration(injectorCacheExpiryTime, injectorCacheExpiryTimeUnit)
+            .makeComputingMap(findTemplatesFunction);
+    }
+
+    /**
+     * Retrieve the template files applicable to a given set of plugin module keys.
+     * The templates can be extracted from WebResources and SoyWebResources in the dependency
+     * trees of the given plugin module.
+     */
+    Set<URL> get(String pluginModulesKey)
+    {
+        return templateSetCache.get(pluginModulesKey);
+    }
+
+    void clear()
+    {
+        templateSetCache.clear();
+    }
+
+    private Set<URL> findRequiredTemplates(String pluginModuleKey)
+    {
+        log.debug("Found Soy template files for '{}'", pluginModuleKey);
+        TemplateSetBuilder templateSetBuilder = new TemplateSetBuilder();
+
+        templateSetBuilder.addTemplatesForTree(pluginModuleKey);
+        Set<URL> result = templateSetBuilder.build();
+        log.debug("Found Soy template files for '{}' was {}", pluginModuleKey, result);
+        return result;
+    }
+
+    private class TemplateSetBuilder
+    {
+        private final Set<URL> fileSet = Sets.newHashSet();
+        private final Set<String> alreadyAddedModules = Sets.newHashSet();
+
+        private void addTemplatesForTree(String completeModuleKey)
+        {
+            if (alreadyAddedModules.contains(completeModuleKey))
+                return;
+
+            ModuleDescriptor<?> moduleDescriptor = pluginAccessor.getEnabledPluginModule(completeModuleKey);
+
+            if (moduleDescriptor == null)
+                throw new IllegalStateException("Required plugin module " + completeModuleKey + " was either missing or disabled");
+
+            if (moduleDescriptor instanceof WebResourceModuleDescriptor)
+                addTemplatesForTree((WebResourceModuleDescriptor) moduleDescriptor);
+
+            addSoyTemplateResources(moduleDescriptor);
+
+            alreadyAddedModules.add(completeModuleKey);
+        }
+
+        private void addTemplatesForTree(WebResourceModuleDescriptor webResourceModuleDescriptor)
+        {
+            for (String dependencyModuleKey : webResourceModuleDescriptor.getDependencies())
+            {
+                addTemplatesForTree(dependencyModuleKey);
+            }
+        }
+
+        private void addSoyTemplateResources(ModuleDescriptor<?> moduleDescriptor)
+        {
+            for (ResourceDescriptor resource : moduleDescriptor.getResourceDescriptors())
+            {
+                if (isSoyTemplate(resource))
+                {
+                    URL url = getSoyResourceURL(moduleDescriptor, resource);
+                    if (url != null)
+                    {
+                        this.fileSet.add(url);
+                    }
+                }
+            }
+        }
+
+        private boolean isSoyTemplate(ResourceDescriptor resource)
+        {
+            return resource.getLocation().endsWith(".soy");
+        }
+
+        private URL getSoyResourceURL(ModuleDescriptor moduleDescriptor, ResourceDescriptor resource)
+        {
+            final String sourceParam = resource.getParameter("source");
+            if ("webContextStatic".equalsIgnoreCase(sourceParam))
+            {
+                try
+                {
+                    return servletContextFactory.getServletContext().getResource(resource.getLocation());
+                }
+                catch (MalformedURLException e)
+                {
+                    log.error("Ignoring soy resource. Could not locate soy with location: " + resource.getLocation());
+                    return null;
+                }
+            }
+            return moduleDescriptor.getPlugin().getResource(resource.getLocation());
+        }
+
+        private Set<URL> build()
+        {
+            return ImmutableSet.copyOf(fileSet);
+        }
+    }
+}

soy-template-plugin/src/test/java/com/atlassian/soy/impl/SoyDependencyInjectorFactoryTest.java

 package com.atlassian.soy.impl;
 
-import com.atlassian.soy.renderer.SoyDataMapper;
-import com.atlassian.soy.renderer.SoyServerFunction;
 import com.google.common.collect.ImmutableList;
 import com.google.inject.Injector;
 import com.google.inject.Module;
-import com.google.template.soy.SoyFileSet;
 import com.google.template.soy.SoyModule;
-import com.google.template.soy.jssrc.SoyJsSrcOptions;
-import com.google.template.soy.jssrc.restricted.JsExpr;
-import com.google.template.soy.jssrc.restricted.SoyJsSrcFunction;
-import com.google.template.soy.shared.restricted.SoyFunction;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.*;
-import org.mockito.runners.*;
 
-import java.net.URL;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertSame;
 
-@RunWith(MockitoJUnitRunner.class)
 public class SoyDependencyInjectorFactoryTest
 {
-    private static final Set<SoyServerFunction<?>> EMPTY_SOY_SERVER_FUNCTIONS = Collections.emptySet();
-    private static final Set<SoyFunction> EMPTY_SOY_FUNCTIONS = Collections.emptySet();
-    private static final String FOO_TEMPLATE_WITH_FUNC_OUTPUT = "// This file was automatically generated from foo.soy.\n" +
-        "// Please don't edit this file by hand.\n" +
-        "\n" +
-        "if (typeof foo == 'undefined') { var foo = {}; }\n" +
-        "\n" +
-        "\n" +
-        "foo.bar = function(opt_data, opt_sb) {\n" +
-        "  var output = opt_sb || new soy.StringBuilder();\n" +
-        "  output.append(soy.$$escapeHtml(MYJS));\n" +
-        "  return opt_sb ? '' : output.toString();\n" +
-        "};\n";
-    @Mock
-    private SoyDataConverter sdc = new SoyDataConverter(Collections.<SoyDataMapper>emptyList());
     private SoyDependencyInjectorFactory injectorFactory;
-    private static final Set<URL> EMPTY_URLS = Collections.emptySet();
-    private Set<SoyFunction> fooFunctionSet;
-    private Set<SoyFunction> barFunctionSet;
 
     @Before
     public void setUp()
     {
         Iterable<Module> defaultModules = ImmutableList.<Module>of(new SoyModule());
-        injectorFactory = new SoyDependencyInjectorFactory(defaultModules, sdc, 30, TimeUnit.DAYS);
-        fooFunctionSet = Collections.<SoyFunction>singleton(makeJsFunc("foo"));
-        barFunctionSet = Collections.<SoyFunction>singleton(makeJsFunc("bar"));
+        injectorFactory = new SoyDependencyInjectorFactory(defaultModules);
     }
 
     @Test
-    public void testInjectorCacheReturnsCorrectInjectors() throws Exception
-    {
-        Injector defaultInjector = getDefaultInjector();
-        Injector fooInjector = getFooInjector();
-        Injector barInjector = getBarInjector();
-        assertNotSame(defaultInjector, fooInjector);
-        assertNotSame(defaultInjector, barInjector);
-        assertNotSame(fooInjector, barInjector);
-        assertSame(defaultInjector, getDefaultInjector());
-        assertSame(fooInjector, getFooInjector());
-        assertSame(barInjector, getBarInjector());
-    }
-
-    private Injector getDefaultInjector()
+    public void testInjectorIsCached() throws Exception
     {
-        return injectorFactory.get(new ModuleData(EMPTY_SOY_SERVER_FUNCTIONS, EMPTY_SOY_FUNCTIONS, EMPTY_URLS));
-    }
-
-    private Injector getFooInjector()
-    {
-        return injectorFactory.get(new ModuleData(EMPTY_SOY_SERVER_FUNCTIONS, fooFunctionSet, EMPTY_URLS));
-    }
-
-    private Injector getBarInjector()
-    {
-        return injectorFactory.get(new ModuleData(EMPTY_SOY_SERVER_FUNCTIONS, barFunctionSet, EMPTY_URLS));
+        Injector defaultInjector = injectorFactory.get();
+        assertSame(defaultInjector, injectorFactory.get());
     }
 
     @Test
-    public void testNoExtraThenExtra() throws Exception
+    public void testClear() throws Exception
     {
-        SoyJsSrcOptions opts = new SoyJsSrcOptions();
-
-        testNoExtraFunctions(opts);
-        testNoExtraFunctions(opts);
-        testExtraFunctionFoo(opts);
-    }
-
-    @Test
-    public void testExtraThenNoExtra() throws Exception
-    {
-        SoyJsSrcOptions opts = new SoyJsSrcOptions();
-
-        testExtraFunctionFoo(opts);
-        testExtraFunctionFoo(opts);
-        testNoExtraFunctions(opts);
-    }
-
-    // SOY-3
-    @Test
-    public void testExtraAndNoExtraInterspersed() throws Exception
-    {
-        SoyJsSrcOptions opts = new SoyJsSrcOptions();
-
-        testExtraFunctionFoo(opts);
-        testNoExtraFunctions(opts);
-        testExtraFunctionFoo(opts);
-        testNoExtraFunctions(opts);
-    }
-
-    // SOY-5
-    // This test fails, because the entire injector caching thing seems to be broken for soy.
-    @Ignore
-    @Test
-    public void testDifferentFunctionSetsInterspersed() throws Exception
-    {
-        SoyJsSrcOptions opts = new SoyJsSrcOptions();
-        testExtraFunctionFoo(opts);
-        testExtraFunctionFoo(opts);
-        testExtraFunctionBar(opts);
-        testExtraFunctionBar(opts);
-        testExtraFunctionFoo(opts);
-        testExtraFunctionFoo(opts);
-    }
-
-    private SoyJsSrcFunction makeJsFunc(final String functionName)
-    {
-        return new SoyJsSrcFunction()
-        {
-            @Override
-            public JsExpr computeForJsSrc(List<JsExpr> args) {
-                return new JsExpr("MYJS", 1);
-            }
-
-            @Override
-            public String getName() {
-                return functionName;
-            }
-
-            @Override
-            public Set<Integer> getValidArgsSizes() {
-                HashSet<Integer> x = new HashSet<Integer>();
-                x.add(0);
-                return x;
-            }
-        };
-    }
-
-    private void testNoExtraFunctions(SoyJsSrcOptions opts)
-    {
-        SoyFileSet.Builder builder = getDefaultInjector().getInstance(SoyFileSet.Builder.class);
-        builder.add("{namespace foo}\n/** */\n{template .bar}\nfoo\n{/template}\n", "foo.soy").build().compileToJsSrc(opts, null);
-    }
-
-    private void testExtraFunctionFoo(SoyJsSrcOptions opts)
-    {
-        Injector injector = getFooInjector();
-        SoyFileSet.Builder builder = injector.getInstance(SoyFileSet.Builder.class);
-
-        List<String> out = builder.add("{namespace foo}\n/** */\n{template .bar}\n{foo()}\n{/template}\n", "foo.soy").build().compileToJsSrc(opts, null);
-        assertEquals(FOO_TEMPLATE_WITH_FUNC_OUTPUT, out.get(0));
-    }
-
-    private void testExtraFunctionBar(SoyJsSrcOptions opts)
-    {
-        Injector injector = getBarInjector();
-        SoyFileSet.Builder builder = injector.getInstance(SoyFileSet.Builder.class);
-
-        List<String> out = builder.add("{namespace foo}\n/** */\n{template .bar}\n{bar()}\n{/template}\n", "foo.soy").build().compileToJsSrc(opts, null);
-        assertEquals(FOO_TEMPLATE_WITH_FUNC_OUTPUT, out.get(0));
+        Injector defaultInjector = injectorFactory.get();
+        injectorFactory.clear();
+        assertNotSame(defaultInjector, injectorFactory.get());
     }
 
 }
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.