package org.ocpsoft.rewrite.transform.markup.impl;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.ServletContext;

import org.jruby.CompatVersion;
import org.jruby.RubyInstanceConfig.CompileMode;
import org.jruby.embed.LocalContextScope;
import org.jruby.embed.LocalVariableBehavior;
import org.jruby.embed.ScriptingContainer;
import org.ocpsoft.rewrite.servlet.http.event.HttpServletRewrite;
import org.ocpsoft.rewrite.transform.StringTransformer;
import org.ocpsoft.rewrite.transform.Transformer;

/**
 * Base class for {@link Transformer} implementations that use JRuby scripts.
 * 
 * @author Christian Kaltepoth
 */
public abstract class JRubyTransformer<T extends JRubyTransformer<T>> extends StringTransformer
{
   static final String CONTAINER_STORE_KEY = JRubyTransformer.class.getName() + "_CONTAINER_INSTANCE";

   private CompileMode compileMode = null;

   private CompatVersion compatVersion = null;

   /**
    * Return the load paths to use for {@link ScriptingContainer#setLoadPaths(List)}.
    */
   public abstract List<String> getLoadPaths();

   /**
    * This method must perform the transformation using the supplied {@link ScriptingContainer}. The container is pre
    * initialized with a variable <code>input</code> which should be used by the script to perform the transformation.
    */
   public abstract Object runScript(ScriptingContainer container);

   /**
    * Just returns the current object as the correct type for make the fluent builder work with subclasses.
    */
   public abstract T self();

   public JRubyTransformer()
   {}

   @Override
   public final String transform(HttpServletRewrite event, String input)
   {
      ScriptingContainer container = getContainer(event.getServletContext());
      try {

         Object result = null;
         // 'input' will be the string to transform
         container.put("input", input);

         // execute the script returned by the implementation
         result = runScript(container);

         // the result must be a string
         return result != null ? result.toString() : null;
      }
      finally {
         if (container != null)
            container.clear();
      }

   }

   private ScriptingContainer getContainer(ServletContext context)
   {
      @SuppressWarnings("unchecked")
      Map<Class<T>, ScriptingContainer> storage = (Map<Class<T>, ScriptingContainer>) context
               .getAttribute(CONTAINER_STORE_KEY);
      if (storage == null)
      {
         storage = new ConcurrentHashMap<Class<T>, ScriptingContainer>();
         context.setAttribute(CONTAINER_STORE_KEY, storage);
      }

      ScriptingContainer cachedContainer = storage.get(getTransformerType());
      if (cachedContainer == null)
      {
         cachedContainer = new ScriptingContainer(LocalContextScope.THREADSAFE, LocalVariableBehavior.TRANSIENT);
         storage.put(getTransformerType(), cachedContainer);

         // the user may have set a custom CompileMode
         if (compileMode != null) {
            cachedContainer.setCompileMode(compileMode);
         }

         // the user may have set a customn CompatVersion
         if (compatVersion != null) {
            cachedContainer.setCompatVersion(compatVersion);
         }

         // scripts typically need to set the load path for 3rd party gems
         List<String> loadPaths = getLoadPaths();
         if (loadPaths != null && !loadPaths.isEmpty()) {
            cachedContainer.setLoadPaths(loadPaths);
         }

         // perform custom initialization of the container
         prepareContainer(cachedContainer);
      }
      
      return cachedContainer;
   }

   abstract protected Class<T> getTransformerType();

   abstract protected void prepareContainer(ScriptingContainer container);

   /**
    * Allows to customize the {@link CompileMode} used by the JRuby runtime.
    */
   protected T compileMode(CompileMode compileMode)
   {
      this.compileMode = compileMode;
      return self();
   }

   /**
    * Allows to customize the {@link CompatVersion} used by the JRuby runtime.
    */
   protected T compatVersion(CompatVersion compatVersion)
   {
      this.compatVersion = compatVersion;
      return self();
   }

}

@@ -2868,18 +2868,18 @@
 ope.
-THREADSAFE
+CONCURRENT
 , Lo

Use concurrent ScriptingContainer