/**
 * Copyright@2011 wro4j
 */
package ro.isdc.wro.model.resource.processor.factory;

import static org.apache.commons.lang3.Validate.notNull;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.imageio.spi.ServiceRegistry;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ro.isdc.wro.model.resource.processor.ResourcePostProcessor;
import ro.isdc.wro.model.resource.processor.ResourcePreProcessor;
import ro.isdc.wro.model.resource.processor.decorator.ExtensionsAwareProcessorDecorator;
import ro.isdc.wro.model.resource.processor.decorator.ProcessorDecorator;
import ro.isdc.wro.model.resource.processor.support.ProcessorProvider;
import ro.isdc.wro.model.resource.support.AbstractConfigurableMultipleStrategy;

/**
 * A {@link ProcessorsFactory} implementation which is easy to configure using a {@link Properties} object.
 *
 * @author Alex Objelean
 * @created 30 Jul 2011
 * @since 1.4.0
 */
public class ConfigurableProcessorsFactory
    implements ProcessorsFactory {
  private static final Logger LOG = LoggerFactory.getLogger(ConfigurableProcessorsFactory.class);

  /**
   * Property name to specify pre processors.
   */
  public static final String PARAM_PRE_PROCESSORS = "preProcessors";
  /**
   * Property name to specify post processors.
   */
  public static final String PARAM_POST_PROCESSORS = "postProcessors";
  private volatile Map<String, ResourcePreProcessor> preProcessorsMap;
  private volatile Map<String, ResourcePostProcessor> postProcessorsMap;

  /**
   * Holds the configurations describing available processors.
   */
  private Properties properties;

  private final AbstractConfigurableMultipleStrategy<ResourcePreProcessor, ProcessorProvider> configurablePreProcessors = createConfigurablePreProcessorsStrategy();

  private final AbstractConfigurableMultipleStrategy<ResourcePostProcessor, ProcessorProvider> configurablePostProcessors = createConfigurablePostProcessorsStrategy();

  /**
   * @return the {@link AbstractConfigurableMultipleStrategy} responsible for providing postProcessors strategies.
   */
  private AbstractConfigurableMultipleStrategy<ResourcePostProcessor, ProcessorProvider> createConfigurablePostProcessorsStrategy() {
    return new AbstractConfigurableMultipleStrategy<ResourcePostProcessor, ProcessorProvider>() {
      @Override
      protected String getStrategyKey() {
        return PARAM_POST_PROCESSORS;
      }

      @Override
      protected void overrideDefaultStrategyMap(final Map<String, ResourcePostProcessor> map) {
        copyAll(ConfigurableProcessorsFactory.this.getPostProcessorsMap(), map);
      }

      @Override
      protected Map<String, ResourcePostProcessor> getStrategies(final ProcessorProvider provider) {
        return getPostProcessorStrategies(provider);
      }

      @Override
      protected ResourcePostProcessor getStrategyForAlias(final String alias) {
        ResourcePostProcessor processor = super.getStrategyForAlias(alias);
        if (processor == null) {
          final String extension = FilenameUtils.getExtension(alias);
          final boolean hasExtension = !StringUtils.isEmpty(extension);
          if (hasExtension) {
            final String processorName = FilenameUtils.getBaseName(alias);
            LOG.debug("processorName: {}", processorName);
            processor = super.getStrategyForAlias(processorName);
            if (processor != null) {
              LOG.debug("adding Extension: {}", extension);
              processor = ExtensionsAwareProcessorDecorator.decorate(new ProcessorDecorator(processor)).addExtension(
                  extension);
            }
          }
        }
        return processor;
      }

      @Override
      protected Properties newProperties() {
        return ConfigurableProcessorsFactory.this.getProperties();
      }
    };
  }

  /**
   * @return the {@link AbstractConfigurableMultipleStrategy} responsible for providing preProcessors strategies.
   */
  private  AbstractConfigurableMultipleStrategy<ResourcePreProcessor, ProcessorProvider> createConfigurablePreProcessorsStrategy() {
    return new AbstractConfigurableMultipleStrategy<ResourcePreProcessor, ProcessorProvider>() {
      @Override
      protected String getStrategyKey() {
        return PARAM_PRE_PROCESSORS;
      }

      @Override
      protected void overrideDefaultStrategyMap(final Map<String, ResourcePreProcessor> map) {
        copyAll(ConfigurableProcessorsFactory.this.getPreProcessorsMap(), map);
      }

      @Override
      protected Map<String, ResourcePreProcessor> getStrategies(final ProcessorProvider provider) {
        return getPreProcessorStrategies(provider);
      }

      @Override
      protected ResourcePreProcessor getStrategyForAlias(final String alias) {
        ResourcePreProcessor processor = super.getStrategyForAlias(alias);
        if (processor == null) {
          final String extension = FilenameUtils.getExtension(alias);
          final boolean hasExtension = !StringUtils.isEmpty(extension);
          if (hasExtension) {
            final String processorName = FilenameUtils.getBaseName(alias);
            LOG.debug("processorName: {}", processorName);
            processor = super.getStrategyForAlias(processorName);
            if (processor != null) {
              LOG.debug("adding Extension: {}", extension);
              processor = ExtensionsAwareProcessorDecorator.decorate(processor).addExtension(extension);
            }
          }
        }
        return processor;
      }

      @Override
      protected Properties newProperties() {
        return ConfigurableProcessorsFactory.this.getProperties();
      };
    };
  }

  /**
   * @return default implementation of {@link Properties} containing the list of pre & post processors.
   */
  protected Properties newProperties() {
    return new Properties();
  }

  /**
   * {@inheritDoc}
   */
  public final Collection<ResourcePreProcessor> getPreProcessors() {
    return configurablePreProcessors.getConfiguredStrategies();
  }

  /**
   * {@inheritDoc}
   */
  public final Collection<ResourcePostProcessor> getPostProcessors() {
    return configurablePostProcessors.getConfiguredStrategies();
  }

  /**
   * @param map
   *          containing preProcessors with corresponding alias (as key). The map must not be null and once set, the
   *          default map will be overridden.
   */
  public ConfigurableProcessorsFactory setPreProcessorsMap(final Map<String, ResourcePreProcessor> map) {
    notNull(map);
    this.preProcessorsMap = map;
    return this;
  }

  /**
   * @param map
   *          containing postProcessors with corresponding alias (as key). The map must not be null and once set, the
   *          default map will be overridden.
   */
  public ConfigurableProcessorsFactory setPostProcessorsMap(final Map<String, ResourcePostProcessor> map) {
    notNull(map);
    this.postProcessorsMap = map;
    return this;
  }

  public ConfigurableProcessorsFactory setProperties(final Properties properties) {
    notNull(properties);
    this.properties = properties;
    return this;
  }

  /**
   * By default the processor will be discovered using {@link ServiceRegistry} pattern (by inspecting META-INF/services
   * folder of each dependency).
   *
   * @return a default map of preProcessors.
   */
  protected Map<String, ResourcePreProcessor> newPreProcessorsMap() {
    return new HashMap<String, ResourcePreProcessor>();
  }

  /**
   * By default the processor will be discovered using {@link ServiceRegistry} pattern (by inspecting META-INF/services
   * folder of each dependency).
   *
   * @return a default map of postProcessors.
   */
  protected Map<String, ResourcePostProcessor> newPostProcessorsMap() {
    return new HashMap<String, ResourcePostProcessor>();
  }

  /**
   * To be used for internal usage. Ensure that returned object is not null.
   */
  private Properties getProperties() {
    if (this.properties == null) {
      this.properties = newProperties();
    }
    return this.properties;
  }

  /**
   * To be used for internal usage. Ensure that returned object is not null.
   */
  private Map<String, ResourcePreProcessor> getPreProcessorsMap() {
    if (this.preProcessorsMap == null) {
      synchronized (this) {
        if (this.preProcessorsMap == null) {
          this.preProcessorsMap = newPreProcessorsMap();
        }
      }
    }
    return this.preProcessorsMap;
  }

  /**
   * To be used for internal usage. Ensure that returned object is not null.
   */
  private Map<String, ResourcePostProcessor> getPostProcessorsMap() {
    if (this.postProcessorsMap == null) {
      synchronized (this) {
        if (this.postProcessorsMap == null) {
          this.postProcessorsMap = newPostProcessorsMap();
        }
      }
    }
    return this.postProcessorsMap;
  }

  /**
   * @VisibleForTesting
   * @return the list of all available pre processors;
   */
  Collection<ResourcePreProcessor> getAvailablePreProcessors() {
    return configurablePreProcessors.getAvailableStrategies();
  }

  /**
   * @VisibleForTesting
   * @return the list of all available pre processors;
   */
  Collection<ResourcePostProcessor> getAvailablePostProcessors() {
    return configurablePostProcessors.getAvailableStrategies();
  }

  protected Map<String, ResourcePostProcessor> getPostProcessorStrategies(final ProcessorProvider provider) {
    return provider.providePostProcessors();
  }

  protected Map<String, ResourcePreProcessor> getPreProcessorStrategies(final ProcessorProvider provider) {
    return provider.providePreProcessors();
  }
}


/**
 * Copyright Alex Objelean
 */
package ro.isdc.wro.extensions.model.factory;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Type;

import org.apache.commons.io.input.AutoCloseInputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ro.isdc.wro.WroRuntimeException;
import ro.isdc.wro.model.WroModel;
import ro.isdc.wro.model.factory.AbstractWroModelFactory;
import ro.isdc.wro.util.StopWatch;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

/**
 * TODO: validate duplicate groups & null resource type
 *

 * Creates {@link WroModel} from a json.
 *

 * This class is thread-safe because it doesn't have any state..
 *
 * @author Alex Objelean
 * @created 13 Mar 2011
 * @since 1.3.6
 */
public class JsonModelFactory
  extends AbstractWroModelFactory {
  private static final Logger LOG = LoggerFactory.getLogger(JsonModelFactory.class);
  /**
   * Default name of the file used to retrieve the model.
   */
  private static final String DEFAULT_FILE_NAME = "wro.json";
  /**
   * Alias for this model factory used by provider.
   */
  public static final String ALIAS = "json";

  /**
   * {@inheritDoc}
   */
  @Override
  public synchronized WroModel create() {
    final StopWatch stopWatch = new StopWatch("Create Wro Model from Groovy");
    try {
      stopWatch.start("createModel");
      final Type type = new TypeToken<WroModel>() {}.getType();
      final InputStream is = getModelResourceAsStream();
      if (is == null) {
        throw new WroRuntimeException("Invalid model stream provided!");
      }
      final WroModel model = new Gson().fromJson(new InputStreamReader(new AutoCloseInputStream(is)), type);
      LOG.debug("json model: {}", model);
      if (model == null) {
        throw new WroRuntimeException("Invalid content provided, cannot build model!");
      }
      return model;
    } catch (final Exception e) {
      throw new WroRuntimeException("Invalid model found!", e);
    } finally {
      stopWatch.stop();
      LOG.debug(stopWatch.prettyPrint());
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected String getDefaultModelFilename() {
    return DEFAULT_FILE_NAME;
  }
}


/**
 * Copyright@2011 wro4j
 */
package ro.isdc.wro.extensions.model.factory;

import static org.apache.commons.lang3.Validate.notNull;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ro.isdc.wro.WroRuntimeException;
import ro.isdc.wro.manager.factory.standalone.StandaloneContext;
import ro.isdc.wro.model.WroModel;
import ro.isdc.wro.model.factory.AbstractWroModelFactory;
import ro.isdc.wro.model.factory.LazyWroModelFactoryDecorator;
import ro.isdc.wro.model.factory.WroModelFactory;
import ro.isdc.wro.model.factory.XmlModelFactory;
import ro.isdc.wro.model.group.Inject;
import ro.isdc.wro.model.group.processor.Injector;
import ro.isdc.wro.util.LazyInitializer;
import ro.isdc.wro.util.WroUtil;

/**
 * When used, this factory will make it possible to migrate easily from xml to groovy (or json) DSL seamlessly.
 *
 * @author Alex Objelean
 * @created 6 Aug 2011
 * @since 1.4.0
 */
public class SmartWroModelFactory
    extends AbstractWroModelFactory {
  private static final Logger LOG = LoggerFactory.getLogger(SmartWroModelFactory.class);
  /**
   * The default location of the wro model file.
   */
  private static final String DEFAULT_WRO_FILE = "/src/main/webapp/WEB-INF/wro.xml";
  /**
   * Alias for this model factory used by provider.
   */
  public static final String ALIAS = "smart";
  @Inject
  private Injector injector;
  private List<WroModelFactory> factoryList;
  /**
   * The exact file where the model is located.
   */
  private File wroFile;
  /**
   * flag indicating if the wroFile should be auto detected.
   */
  private boolean autoDetectWroFile = true;

  /**
   * Use this factory method when you want to use the {@link SmartWroModelFactory} in standalone (maven plugin) context.
   * The autoDetect flag is set to true if the wroFile path is the same as the default wro file name.
   */
  public static SmartWroModelFactory createFromStandaloneContext(final StandaloneContext context) {
    notNull(context);
    final boolean autoDetectWroFile = WroUtil.normalize(context.getWroFile().getPath()).contains(
        WroUtil.normalize(DEFAULT_WRO_FILE));
    if (!autoDetectWroFile) {
      LOG.debug("autoDetect is " + autoDetectWroFile + " because wroFile: " + context.getWroFile()
          + " is not the same as the default one: " + DEFAULT_WRO_FILE);
    }
    return new SmartWroModelFactory().setWroFile(context.getWroFile()).setAutoDetectWroFile(autoDetectWroFile);
  }

  /**
   * The file to use for creating model. It is not required to set this field, but if you set, do not set a null object.
   *
   * @param wroFile
   *          the wroFile to set
   */
  public SmartWroModelFactory setWroFile(final File wroFile) {
    Validate.notNull(wroFile);
    this.wroFile = wroFile;
    return this;
  }

  /**
   * @return default list of factories to be used by {@link SmartWroModelFactory}.
   */
  protected List<WroModelFactory> newWroModelFactoryFactoryList() {
    final List<WroModelFactory> list = new ArrayList<WroModelFactory>();
    LOG.debug("auto detect wroFile: {}", autoDetectWroFile);
    list.add(newXmlModelFactory());
    list.add(newGroovyModelFactory());
    list.add(newJsonModelFactory());
    return list;
  }

  private XmlModelFactory newXmlModelFactory() {
    return new XmlModelFactory() {
      @Override
      protected InputStream getModelResourceAsStream()
          throws IOException {
        if (wroFile == null) {
          return super.getModelResourceAsStream();
        }
        return createAutoDetectedStream(getDefaultModelFilename());
      }
    };
  }

  private WroModelFactory newGroovyModelFactory() {
    return new LazyWroModelFactoryDecorator(new LazyInitializer<WroModelFactory>() {
      @Override
      protected WroModelFactory initialize() {
        final WroModelFactory modelFactory = new GroovyModelFactory() {
          @Override
          protected InputStream getModelResourceAsStream()
              throws IOException {
            if (wroFile == null) {
              return super.getModelResourceAsStream();
            }
            return createAutoDetectedStream(getDefaultModelFilename());
          }
        };
        injector.inject(modelFactory);
        return modelFactory;
      }
    });
  }

  private WroModelFactory newJsonModelFactory() {
    return new LazyWroModelFactoryDecorator(new LazyInitializer<WroModelFactory>() {
      @Override
      protected WroModelFactory initialize() {
        final WroModelFactory modelFactory = new JsonModelFactory() {
          @Override
          protected InputStream getModelResourceAsStream()
              throws IOException {
            if (wroFile == null) {
              return super.getModelResourceAsStream();
            }
            return createAutoDetectedStream(getDefaultModelFilename());
          }
        };
        injector.inject(modelFactory);
        return modelFactory;
      }
    });
  }

  /**
   * Handles the resource model auto detection.
   */
  private InputStream createAutoDetectedStream(final String defaultFileName)
      throws IOException {
    try {
      Validate.notNull(wroFile, "Cannot call this method if wroFile is null!");
      if (autoDetectWroFile) {
        final File file = new File(wroFile.getParentFile(), defaultFileName);
        LOG.debug("\tloading autodetected wro file: " + file);
        return new FileInputStream(file);
      }
      LOG.debug("loading wroFile: " + wroFile);
      return new FileInputStream(wroFile);
    } catch (final FileNotFoundException e) {
      // When auto detect is turned on, do not skip trying.. because the auto detection assume that the wro file name
      // can be wrong.
      if (autoDetectWroFile) {
        throw e;
      }
      throw new WroRuntimeException("The wroFile doesn't exist. Skip trying with other wro model factories", e);
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public WroModel create() {
    if (factoryList == null) {
      factoryList = newWroModelFactoryFactoryList();
    }
    if (factoryList != null) {
      // Holds the details about model creation which are logged only when no model can be created
      final StringBuffer logMessageBuffer = new StringBuffer();
      for (final WroModelFactory factory : factoryList) {
        try {
          // use injector for aggregated modelFactories
          injector.inject(factory);
          final Class<? extends WroModelFactory> factoryClass = factory.getClass().asSubclass(WroModelFactory.class);
          logMessageBuffer.append(" Using " + getClassName(factoryClass) + " for model creation..\n");
          return factory.create();
        } catch (final WroRuntimeException e) {
          LOG.debug("[FAIL] creating model... will try another factory: {}", e.getCause());
          logMessageBuffer.append("[FAIL] Model creation using " + getClassName(factory.getClass())
              + " failed. Trying another ...\n");
          logMessageBuffer.append("[FAIL] Exception occured while building the model using: "
              + getClassName(factory.getClass()) + " " + e.getMessage());
          // stop trying with other factories if the reason is IOException
          if (!autoDetectWroFile && e.getCause() instanceof IOException) {
            throw e;
          }
        }
      }
      LOG.error(logMessageBuffer.toString());
    }
    throw new WroRuntimeException("Cannot create model using any of provided factories");
  }

  /**
   * @return string representation of the factory name.
   */
  protected String getClassName(final Class<? extends WroModelFactory> factoryClass) {
    return factoryClass.isAnonymousClass() ? factoryClass.getSuperclass().getSimpleName()
        : factoryClass.getSimpleName();
  }

  /**
   * @param factoryList
   *          the factoryList to set
   */
  public SmartWroModelFactory setFactoryList(final List<WroModelFactory> factoryList) {
    Validate.notNull(factoryList);
    this.factoryList = factoryList;
    return this;
  }

  /**
   * In order to keep backward compatibility for building the model . The idea is if this field is false, then the exact
   * file will be used to create the model, otherwise, wro model file is auto-detected based on the parent folder where
   * the wroFile is located.
   *
   * @param autoDetectWroFile
   *          the autoDetectWroFile to set
   */
  public SmartWroModelFactory setAutoDetectWroFile(final boolean autoDetectWroFile) {
    this.autoDetectWroFile = autoDetectWroFile;
    return this;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected String getDefaultModelFilename() {
    return DEFAULT_WRO_FILE;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected InputStream getModelResourceAsStream()
      throws IOException {
    throw new IllegalStateException("This method should never be called!");
  }
}


package ro.isdc.wro.model.resource.processor.support;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang3.StringUtils;

import ro.isdc.wro.util.WroUtil;

/**
 * Encapsulates the matcher creation for css import statements detection. Useful to isolate unit tests. TODO prefer a
 * fluent interface implementation for this class.
 *
 * @author Alex Objelean
 * @created 19 Feb 2013
 * @since 1.6.3
 */
public class CssImportInspector {
  private static final Pattern PATTERN = Pattern.compile(WroUtil.loadRegexpWithKey("cssImport"));
  private static final String REGEX_IMPORT_FROM_COMMENTS = WroUtil.loadRegexpWithKey("cssImportFromComments");
  /**
   * The index of the url group in regex.
   */
  private static final int INDEX_URL = 1;
  private final Matcher matcher;

  public CssImportInspector(final String cssContent) {
    matcher = getMatcher(removeImportsFromComments(cssContent));
  }

  /**
   * @return a {@link Matcher} for the processed content using the regexp responsible for identifying css import
   * statements.
   */
  protected Matcher getMatcher(final String cssContent) {
    return PATTERN.matcher(cssContent);
  }

  /**
   * Removes all @import statements from css.
   */
  public final String removeImportStatements() {
    final StringBuffer sb = new StringBuffer();
    while (matcher.find()) {
      // replace @import with empty string
      matcher.appendReplacement(sb, StringUtils.EMPTY);
    }
    matcher.appendTail(sb);
    return sb.toString();
  }

  /**
   * @return true if checked css content contains an @import statement.
   */
  public final boolean containsImport() {
    return matcher.find();
  }

  /**
   * @return a list of all resources imported using @import statement.
   */
  public final List<String> findImports() {
    final List<String> list = new ArrayList<String>();
    while (matcher.find()) {
      list.add(extractImportUrl(matcher));
    }
    return list;
  }

  /**
   * Override this method to provide a custom way of extracting the imported resource url.
   *
   * @param matcher
   *          the {@link Matcher} inspecting the parsed css content.
   * @return the url of the imported resources.
   */
  protected String extractImportUrl(final Matcher matcher) {
    return matcher.group(INDEX_URL);
  }

  /**
   * Removes all import statements from provided css content.
   *
   * @return the css content with import statement removed.
   * @VisibleForTesting
   */
  final String removeImportsFromComments(final String content) {
    return content.replaceAll(REGEX_IMPORT_FROM_COMMENTS, StringUtils.EMPTY);
  }
}


package ro.isdc.wro.model.resource.processor.support;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import ro.isdc.wro.model.resource.processor.ResourcePostProcessor;
import ro.isdc.wro.model.resource.processor.ResourcePreProcessor;
import ro.isdc.wro.model.resource.processor.decorator.ProcessorDecorator;
import ro.isdc.wro.model.resource.processor.impl.MultiLineCommentStripperProcessor;
import ro.isdc.wro.model.resource.processor.impl.SingleLineCommentStripperProcessor;
import ro.isdc.wro.model.resource.processor.impl.css.ConformColorsCssProcessor;
import ro.isdc.wro.model.resource.processor.impl.css.CssCompressorProcessor;
import ro.isdc.wro.model.resource.processor.impl.css.CssDataUriPreProcessor;
import ro.isdc.wro.model.resource.processor.impl.css.CssImportPreProcessor;
import ro.isdc.wro.model.resource.processor.impl.css.CssMinProcessor;
import ro.isdc.wro.model.resource.processor.impl.css.CssUrlAuthorizationProcessor;
import ro.isdc.wro.model.resource.processor.impl.css.CssUrlRewritingProcessor;
import ro.isdc.wro.model.resource.processor.impl.css.CssVariablesProcessor;
import ro.isdc.wro.model.resource.processor.impl.css.DuplicatesAwareCssDataUriPreProcessor;
import ro.isdc.wro.model.resource.processor.impl.css.FallbackCssDataUriProcessor;
import ro.isdc.wro.model.resource.processor.impl.css.JawrCssMinifierProcessor;
import ro.isdc.wro.model.resource.processor.impl.css.LessCssImportPreProcessor;
import ro.isdc.wro.model.resource.processor.impl.css.VariablizeColorsCssProcessor;
import ro.isdc.wro.model.resource.processor.impl.js.ConsoleStripperProcessor;
import ro.isdc.wro.model.resource.processor.impl.js.JSMinProcessor;
import ro.isdc.wro.model.resource.processor.impl.js.SemicolonAppenderPreProcessor;

/**
 * The implementation which contributes with processors from core module.
 *
 * @author Alex Objelean
 * @created 1 Jun 2012
 */
public class DefaultProcessorProvider
    implements ProcessorProvider {
  /**
   * {@inheritDoc}
   */
  public Map<String, ResourcePreProcessor> providePreProcessors() {
    final Map<String, ResourcePreProcessor> map = new HashMap<String, ResourcePreProcessor>();
    populateProcessorsMap(map);
    return map;
  }

  /**
   * {@inheritDoc}
   */
  public Map<String, ResourcePostProcessor> providePostProcessors() {
    return toPostProcessors(providePreProcessors());
  }

  /**
   * Creates a map of postProcessors form a map of preProcessors. This method will be removed in 1.5.0 release when
   * there will be no differences between pre & post processor interface.
   */
  private Map<String, ResourcePostProcessor> toPostProcessors(
      final Map<String, ResourcePreProcessor> preProcessorsMap) {
    final Map<String, ResourcePostProcessor> map = new HashMap<String, ResourcePostProcessor>();
    for (final Entry<String, ResourcePreProcessor> entry : preProcessorsMap.entrySet()) {
      map.put(entry.getKey(), new ProcessorDecorator(entry.getValue()));
    }
    return map;
  }

  private void populateProcessorsMap(final Map<String, ResourcePreProcessor> map) {
    map.put(CssUrlRewritingProcessor.ALIAS, new CssUrlRewritingProcessor());
    map.put(CssImportPreProcessor.ALIAS, new CssImportPreProcessor());
    map.put(LessCssImportPreProcessor.ALIAS, new LessCssImportPreProcessor());
    map.put(CssVariablesProcessor.ALIAS, new CssVariablesProcessor());
    map.put(CssCompressorProcessor.ALIAS, new CssCompressorProcessor());
    map.put(SemicolonAppenderPreProcessor.ALIAS, new SemicolonAppenderPreProcessor());
    map.put(CssDataUriPreProcessor.ALIAS, new CssDataUriPreProcessor());
    map.put(FallbackCssDataUriProcessor.ALIAS, new FallbackCssDataUriProcessor());
    map.put(DuplicatesAwareCssDataUriPreProcessor.ALIAS_DUPLICATE, new DuplicatesAwareCssDataUriPreProcessor());
    map.put(JawrCssMinifierProcessor.ALIAS, new JawrCssMinifierProcessor());
    map.put(CssMinProcessor.ALIAS, new CssMinProcessor());
    map.put(JSMinProcessor.ALIAS, new JSMinProcessor());
    map.put(VariablizeColorsCssProcessor.ALIAS, new VariablizeColorsCssProcessor());
    map.put(ConformColorsCssProcessor.ALIAS, new ConformColorsCssProcessor());
    map.put(SingleLineCommentStripperProcessor.ALIAS, new SingleLineCommentStripperProcessor());
    map.put(MultiLineCommentStripperProcessor.ALIAS, new MultiLineCommentStripperProcessor());
    map.put(ConsoleStripperProcessor.ALIAS, new ConsoleStripperProcessor());
    map.put(CssUrlAuthorizationProcessor.ALIAS, new CssUrlAuthorizationProcessor());
  }
}


package ro.isdc.wro.util.concurrent;

import java.util.Collection;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ro.isdc.wro.util.StopWatch;

/**
 * Hides the details of running tasks in parallel.
 *
 * @author Alex Objelean
 * @since 1.7.1
 * @date 26 Aug 2013
 */
public class TaskExecutor<T> {
  private static final Logger LOG = LoggerFactory.getLogger(TaskExecutor.class);
  /**
   * Run the tasks in parallel.
   */
  private CompletionService<T> completionService;
  /**
   * Responsible for consume the available results. The consumer is run asynchronously to avoid blocking the job
   * execution.
   */
  private ExecutorService completionExecutor;

  private CompletionService<T> getCompletionService() {
    if (completionService == null) {
      completionService = new ExecutorCompletionService<T>(getExecutor());
    }
    return completionService;
  }

  private ExecutorService getExecutor() {
    if (completionExecutor == null) {
      completionExecutor = newExecutor();
    }
    return completionExecutor;
  }

  /**
   * The implementation uses jsr166 ForkJoinPool implementation in case it is available and can be used,
   * otherwise the default {@link ExecutorService} is used.
   *
   * @return the {@link ExecutorService} responsible for running the tasks.
   */
  protected ExecutorService newExecutor() {
    try {
      final ExecutorService executor = (ExecutorService) Class.forName("java.util.concurrent.ForkJoinPool").newInstance();
      LOG.debug("Using ForkJoinPool as task executor.");
      return executor;
    } catch (final Exception e) {
      LOG.debug("ForkJoinPool class is not available, using default executor.", e);
      return Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    }
  }

  /**
   * TODO rename to submitAll.
   * <p/>
   * Submits a chunk of jobs for parallel execution. This is a blocking operation - it will end execution when all
   * submitted tasks are finished.
   *
   * @param callables
   *          a {@link Collection} of {@link Callable} to execute. When the provided collection contains only one item,
   *          the task will be executed synchronously.
   * @throws Exception
   *           if an exception occurred during callable execution.
   */
  public void submit(final Collection<Callable<T>> callables)
      throws Exception {
    Validate.notNull(callables);

    final StopWatch watch = new StopWatch();
    watch.start("init");
    final long start = System.currentTimeMillis();
    final AtomicLong totalTime = new AtomicLong();
    LOG.debug("running {} tasks", callables.size());

    if (callables.size() == 1) {
      final T result = callables.iterator().next().call();
      onResultAvailable(result);
    } else {
      LOG.debug("Running tasks in parallel");
      watch.stop();

      watch.start("submit tasks");
      for (final Callable<T> callable : callables) {
        getCompletionService().submit(decorate(callable, totalTime));
      }
      watch.stop();

      watch.start("consume results");
      for (int i = 0; i < callables.size(); i++) {
        doConsumeResult();
      }
    }
    watch.stop();
    destroy();

    LOG.debug("Number of Tasks: {}", callables.size());
    final long averageExecutionTime = callables.size() != 0 ? totalTime.longValue() / callables.size() : 0;
    LOG.debug("Average Execution Time: {}", averageExecutionTime);
    LOG.debug("Total Task Time: {}", totalTime);
    LOG.debug("Grand Total Execution Time: {}", System.currentTimeMillis() - start);
    LOG.debug(watch.prettyPrint());
  }

  private Callable<T> decorate(final Callable<T> decorated, final AtomicLong totalTime) {
    return new Callable<T>() {
      public T call()
          throws Exception {
        final long begin = System.currentTimeMillis();
        try {
          return decorated.call();
        } finally {
          final long end = System.currentTimeMillis();
          totalTime.addAndGet(end - begin);
        }
      }
    };
  }

  /**
   * Invoked when an exception occurs during task execution. By default exception is ignored.
   *
   * @param e
   *          {@link Exception} caught during execution.
   */
  protected void onException(final Exception e)
      throws Exception {
  }

  /**
   * A callback invoked when a result is available.
   *
   * @param result
   *          the available processed result.
   * @throws Exception
   *           if callback execution fails.
   */
  protected void onResultAvailable(final T result)
      throws Exception {
  }

  private void doConsumeResult()
      throws Exception {
    try {
      final T result = getCompletionService().take().get();
      onResultAvailable(result);
    } catch (final Exception e) {
      //propagate the most relevant exception
      Exception rootException = e;
      if (e instanceof ExecutionException) {
        if (e.getCause() instanceof Exception) {
          rootException = (Exception) e.getCause();
        }
      }
      onException(rootException);
      LOG.error("Exception while consuming result", e);
    }
  }

  /**
   * Shutdown all executors used by this class.
   */
  public void destroy() {
    if (completionExecutor != null) {
      completionExecutor.shutdown();
    }
  }
}


/**
 * Copyright Alex Objelean
 */
package ro.isdc.wro.model.resource.processor.impl.css;

import java.io.IOException;
import java.io.Reader;
import java.io.Writer;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ro.isdc.wro.model.group.processor.Minimize;
import ro.isdc.wro.model.resource.Resource;
import ro.isdc.wro.model.resource.ResourceType;
import ro.isdc.wro.model.resource.SupportedResourceType;
import ro.isdc.wro.model.resource.processor.ResourcePostProcessor;
import ro.isdc.wro.model.resource.processor.ResourcePreProcessor;
import ro.isdc.wro.model.resource.processor.support.JawrCssMinifier;

/**
 * A processor implementation   using {@link JawrCssMinifier} algorithm. This processor can be used as both: PreProcessor
 * & postProcessor. <br/>
 * This processor is annotated with {@link Minimize} because it performs minimization.
 *
 * @author Alex Objelean
 */
@Minimize
@SupportedResourceType(ResourceType.CSS)
public class JawrCssMinifierProcessor
  implements ResourcePreProcessor, ResourcePostProcessor {
  private static final Logger LOG = LoggerFactory.getLogger(JawrCssMinifierProcessor.class);
  public static final String ALIAS = "cssMinJawr";
  /**
   * {@inheritDoc}
   */
  public void process(final Reader reader, final Writer writer)
    throws IOException {
    process(null, reader, writer);
  }

  /**
   * {@inheritDoc}
   */
  public void process(final Resource resource, final Reader reader, final Writer writer)
    throws IOException {
    try {
      final String content = IOUtils.toString(reader);
      final StringBuffer result = new JawrCssMinifier().minifyCSS(new StringBuffer(content));
      writer.write(result.toString());
      writer.flush();
    } catch (final Exception e) {
      final String resourceUri = resource == null ? StringUtils.EMPTY : "[" + resource.getUri() + "]";
      String message = "Exception while applying " + getClass().getSimpleName() + " processor on the "
          + resourceUri + " resource";
      LOG.error(message, e);
      throw new IOException(message);
    } finally {
      reader.close();
      writer.close();
    }
  }
}


package ro.isdc.wro.extensions.processor.support.template;

import java.io.IOException;
import java.io.InputStream;

import org.apache.commons.lang3.StringUtils;
import org.mozilla.javascript.ScriptableObject;

import ro.isdc.wro.WroRuntimeException;
import ro.isdc.wro.extensions.script.RhinoScriptBuilder;
import ro.isdc.wro.util.WroUtil;

/**
 * A base class for template processors like: dustJS or hoganJS.
 *
 * @author Eivind Barstad Waaler
 * @since 1.4.7
 * @created 11 May 2012
 */
public abstract class AbstractJsTemplateCompiler {
  private ScriptableObject scope;

  /**
   * Compiles the javascript template into plain javascript.
   *
   * @param content
   *          the template to be compiled.
   * @param optionalArgument
   *          any additional arguments used by template script.
   * @return the compiled javascript.
   */
  public String compile(final String content, final String optionalArgument) {
    final RhinoScriptBuilder builder = initScriptBuilder();
    final String argStr = createArgStr(optionalArgument) + createArgStr(getArguments());
    final String compileScript =
      String.format("%s(%s%s);", getCompileCommand(), WroUtil.toJSMultiLineString(content), argStr);
    return (String) builder.evaluate(compileScript, getCompileCommand());
  }

  /**
   * @return the js statement used to execute the compilation of the template.
   */
  protected abstract String getCompileCommand();

  /**
   * @return additional arguments for the compiler.
   */
  protected String getArguments() {
    return null;
  }

  private String createArgStr(final String argument) {
    return StringUtils.isNotBlank(argument) ? ", " + argument : "";
  }

  /**
   * @return the stream of the compiler resource (javascript) used to compile templates.
   * @throws IOException
   *           if the stream could not be located.
   */
  protected abstract InputStream getCompilerAsStream() throws IOException;

  private RhinoScriptBuilder initScriptBuilder() {
    try {
      RhinoScriptBuilder builder;
      if (scope == null) {
        builder = RhinoScriptBuilder.newClientSideAwareChain().evaluateChain(getCompilerAsStream(), "templateCompiler.js");
        scope = builder.getScope();
      } else {
        builder = RhinoScriptBuilder.newChain(scope);
      }
      return builder;
    } catch (final IOException ex) {
      throw new WroRuntimeException("Failed reading init script", ex);
    }
  }
}


package ro.isdc.wro.extensions.processor.js;

import java.io.IOException;
import java.io.Reader;
import java.io.Writer;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;

import ro.isdc.wro.extensions.processor.support.ObjectPoolHelper;
import ro.isdc.wro.extensions.processor.support.template.AbstractJsTemplateCompiler;
import ro.isdc.wro.model.resource.Resource;
import ro.isdc.wro.model.resource.processor.Destroyable;
import ro.isdc.wro.model.resource.processor.ResourcePreProcessor;
import ro.isdc.wro.util.ObjectFactory;

/**
 * A base class for template processors like: dustJS or hoganJS.
 *
 * @author Eivind Barstad Waaler
 * @since 1.4.7
 * @created 11 May 2012
 */
public abstract class JsTemplateCompilerProcessor implements ResourcePreProcessor, Destroyable {
  private final ObjectPoolHelper<AbstractJsTemplateCompiler> enginePool;

  public JsTemplateCompilerProcessor() {
    enginePool = new ObjectPoolHelper<AbstractJsTemplateCompiler>(new ObjectFactory<AbstractJsTemplateCompiler>() {
      @Override
      public AbstractJsTemplateCompiler create() {
        return createCompiler();
      }
    });
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void process(final Resource resource, final Reader reader, final Writer writer) throws IOException {
    final String content = IOUtils.toString(reader);
    final AbstractJsTemplateCompiler jsCompiler = enginePool.getObject();
    try {
      writer.write(jsCompiler.compile(content, getArgument(resource)));
    } finally {
      enginePool.returnObject(jsCompiler);
      reader.close();
      writer.close();
    }
  }

  /**
   * @param resource
   *          {@link Resource} being processed by compiler.
   * @return arguments consumed by the js compile script.
   */
  protected String getArgument(final Resource resource) {
    return resource == null ? "" : FilenameUtils.getBaseName(resource.getUri());
  }

  /**
   * @return the {@link AbstractJsTemplateCompiler} responsible for compiling the template.
   */
  protected abstract AbstractJsTemplateCompiler createCompiler();

  @Override
  public void destroy() throws Exception {
    enginePool.destroy();
  }
}


/**
 * Copyright Alex Objelean
 */
package ro.isdc.wro.model.resource.processor.impl.css;

import static ro.isdc.wro.util.WroUtil.cleanImageUrl;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.CharEncoding;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ro.isdc.wro.WroRuntimeException;
import ro.isdc.wro.http.handler.ResourceProxyRequestHandler;
import ro.isdc.wro.model.group.Inject;
import ro.isdc.wro.model.resource.locator.factory.UriLocatorFactory;
import ro.isdc.wro.model.resource.processor.support.DataUriGenerator;
import ro.isdc.wro.util.WroUtil;

/**
 * Rewrites background images by replacing the url with data uri of the image. If the replacement is not successful, it
 * is left unchanged.
 *

 * Attention: This processor should be added before {@link CssUrlRewritingProcessor}, otherwise the url's won't be
 * replaced. For more details, @see DataUri Scheme on
 * Wikipedia
 *
 * @author Alex Objelean
 * @created May 9, 2010
 */
public class CssDataUriPreProcessor
    extends AbstractCssUrlRewritingProcessor {
  private static final Logger LOG = LoggerFactory.getLogger(CssDataUriPreProcessor.class);
  public static final String ALIAS = "cssDataUri";
  /**
   * The size limit. Images larger than this limit won't be transformed (due to IE8 limitation).
   */
  private static final int SIZE_LIMIT = 32 * 1024;
  /**
   * Generates dataUri based on inputStream of the url's found inside the css resource.
   */
  private DataUriGenerator dataUriGenerator;
  /**
   * Contains a {@link UriLocatorFactory} reference injected externally.
   */
  @Inject
  private UriLocatorFactory uriLocatorFactory;

  /**
   * Replace provided url with the new url if needed.
   *
   * @param imageUrl
   *          to replace.
   * @param cssUri
   *          Uri of the parsed css.
   * @return replaced url.
   */
  @Override
  protected String replaceImageUrl(final String cssUri, final String imageUrl) {
    Validate.notNull(uriLocatorFactory);
    LOG.debug("replace url for image: {} from css: {}", imageUrl, cssUri);
    final String cleanImageUrl = cleanImageUrl(imageUrl);
    final String fileName = FilenameUtils.getName(imageUrl);
    String fullPath = cleanImageUrl;
    /**
     * Allow dataUri transformation of absolute url's using http(s) protocol. All url's protocol are intentionally not
     * allowed, because it could be a potential security issue. For instance:
     *
     *

     * .class {
     *   background: url(file:/path/to/secure/file.png);
     * }
     *

     *
     * This should not be allowed.
     */
    if (isImageUrlChangeRequired(cleanImageUrl)) {
      fullPath = WroUtil.getFullPath(cssUri) + cleanImageUrl;
    }
    String result = imageUrl;
    InputStream is = null;
    try {
      is = uriLocatorFactory.locate(fullPath);
      final String dataUri = getDataUriGenerator().generateDataURI(is, fileName);
      if (isReplaceAccepted(dataUri)) {
        result = dataUri;
        LOG.debug("dataUri replacement: {}", StringUtils.abbreviate(dataUri, 30));
      }
    } catch (final IOException e) {
      LOG.warn("[FAIL] extract dataUri from: {}, because: {}. "
          + "A possible cause: using CssUrlRewritingProcessor before CssDataUriPreProcessor.", fullPath, e.getMessage());
    } finally {
      IOUtils.closeQuietly(is);
    }
    return result;
  }

  /**
   * @param imageUrl
   *          the original url of the image.
   * @return true if the image url should be replaced with another (servlet context relative).
   */
  private boolean isImageUrlChangeRequired(final String imageUrl) {
    return !(imageUrl.startsWith("http") || ResourceProxyRequestHandler.isProxyUri(imageUrl));
  }

  /**
   * Similar to {@link CssDataUriPreProcessor#isReplaceAccepted(String)}, but decides whether the computed dataUri
   * should replace the image url. It is useful when you want to limit the dataUri size. By default the size of dataUri
   * is limited to 32KB (because IE8 has a 32KB limitation).
   *
   * @param dataUri
   *          base64 encoded stream.
   * @return true if dataUri should replace original image url.
   */
  protected boolean isReplaceAccepted(final String dataUri) {
    try {
      final byte[] bytes = dataUri.getBytes(CharEncoding.UTF_8);
      final boolean exceedLimit = bytes.length >= SIZE_LIMIT;
      LOG.debug("dataUri size: {}KB, limit exceeded: {}", bytes.length / 1024, exceedLimit);
      return !exceedLimit;
    } catch (final UnsupportedEncodingException e) {
      throw new WroRuntimeException("Should never happen", e);
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected boolean isReplaceNeeded(final String url) {
    // the dataUri should replace also absolute url's
    return !DataUriGenerator.isDataUri(url.trim());
  }

  /**
   * @return the DataUriGenerator class responsible for transforming streams into base64 encoded strings.
   * @VisibleForTesting
   */
  protected DataUriGenerator getDataUriGenerator() {
    if (dataUriGenerator == null) {
      dataUriGenerator = new DataUriGenerator();
    }
    return dataUriGenerator;
  }
}


/**
 * Copyright wro4j@2011
 */
package ro.isdc.wro.model.resource.processor.decorator;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ro.isdc.wro.model.resource.Resource;
import ro.isdc.wro.model.resource.processor.ResourcePostProcessor;
import ro.isdc.wro.model.resource.processor.ResourcePreProcessor;
import ro.isdc.wro.util.WroUtil;

/**
 * Inspects the resource for copyright (licence) header and inserts them back if the decorated processor removes them.
 * The decorated processor does the compression logic and probably removes copyright comments. If copyright comments are
   * removed, our processor will put them back.
   *
 * @author Alex Objelean
 * @created 14 May 2011
 * @since 1.3.7
 */
public class CopyrightKeeperProcessorDecorator
  extends ProcessorDecorator {
  private static final Logger LOG = LoggerFactory.getLogger(CopyrightKeeperProcessorDecorator.class);
  private static final Pattern PATTERN_COPYRIGHT = Pattern.compile(WroUtil.loadRegexpWithKey("comment.copyright"));

  private CopyrightKeeperProcessorDecorator(final ResourcePreProcessor preProcessor) {
    super(preProcessor);
  }

  private CopyrightKeeperProcessorDecorator(final ResourcePostProcessor postProcessor) {
    super(postProcessor);
  }

  public static CopyrightKeeperProcessorDecorator decorate(final ResourcePreProcessor preProcessor) {
    return new CopyrightKeeperProcessorDecorator(preProcessor);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void process(final Resource resource, final Reader reader, final Writer writer)
    throws IOException {
    try {
      final String content = IOUtils.toString(reader);
      final Matcher originalMatcher = PATTERN_COPYRIGHT.matcher(content);
      final StringBuffer copyrightBuffer = new StringBuffer();
      while (originalMatcher.find()) {
        LOG.debug("found copyright comment");
        // add copyright header to the buffer.
        copyrightBuffer.append(originalMatcher.group());
      }

      LOG.debug("buffer: {}", copyrightBuffer);
      final Writer processedWriter = new StringWriter();
      getDecoratedObject().process(resource, new StringReader(content), processedWriter);

      final Matcher processedMatcher = PATTERN_COPYRIGHT.matcher(processedWriter.toString());

      if (!processedMatcher.find()) {
        writer.write(copyrightBuffer.toString());
      }
      writer.write(processedWriter.toString());
    } finally {
      reader.close();
      writer.close();
    }
  }
}


/**
 * Copyright Alex Objelean
 */
package ro.isdc.wro.http;

import java.util.Properties;

import javax.servlet.FilterConfig;

import ro.isdc.wro.config.factory.PropertyWroConfigurationFactory;
import ro.isdc.wro.config.jmx.ConfigConstants;
import ro.isdc.wro.config.jmx.WroConfiguration;
import ro.isdc.wro.manager.factory.DefaultWroManagerFactory;
import ro.isdc.wro.manager.factory.WroManagerFactory;
import ro.isdc.wro.util.ObjectFactory;

/**
 * An extension of {@link WroFilter} which allows configuration by injecting some of the properties. This class can be
 * very useful when using DelegatingFilterProxy (spring extension of Filter) and configuring the fields with values from
 * some properties file which may vary depending on environment.
 *
 * @author Alex Objelean
 */
public class ConfigurableWroFilter
    extends WroFilter {
  /**
   * Properties to be injected with default values set. These values are deprecated. Prefer setting the "properties"
   * field instead.
   */
  @Deprecated
  private boolean debug = true;
  @Deprecated
  private boolean gzipEnabled = true;
  @Deprecated
  private boolean jmxEnabled = true;
  @Deprecated
  private String mbeanName;
  @Deprecated
  private long cacheUpdatePeriod = 0;
  @Deprecated
  private long modelUpdatePeriod = 0;
  @Deprecated
  private boolean disableCache;
  @Deprecated
  private String encoding;

  /**
   * This {@link Properties} object will hold the configurations and it will replace all other fields.
   */
  private Properties properties;

  /**
   * {@inheritDoc}
   */
  @Override
  protected ObjectFactory<WroConfiguration> newWroConfigurationFactory(final FilterConfig filterConfig) {
    if (properties == null) {
      // when no
      properties = new Properties();
      properties.setProperty(ConfigConstants.debug.name(), String.valueOf(debug));
      properties.setProperty(ConfigConstants.gzipResources.name(), String.valueOf(gzipEnabled));
      properties.setProperty(ConfigConstants.jmxEnabled.name(), String.valueOf(jmxEnabled));
      properties.setProperty(ConfigConstants.cacheUpdatePeriod.name(), String.valueOf(cacheUpdatePeriod));
      properties.setProperty(ConfigConstants.modelUpdatePeriod.name(), String.valueOf(modelUpdatePeriod));
      properties.setProperty(ConfigConstants.disableCache.name(), String.valueOf(disableCache));
      if (encoding != null) {
        properties.setProperty(ConfigConstants.encoding.name(), encoding);
      }
      if (mbeanName != null) {
        properties.setProperty(ConfigConstants.mbeanName.name(), mbeanName);
      }
    }
    return new PropertyWroConfigurationFactory(properties);
  }

  /**
   * @param disableCache
   *          the disableCache to set
   */
  public void setDisableCache(final boolean disableCache) {
    this.disableCache = disableCache;
  }

  @Override
  protected String newMBeanName() {
    if (mbeanName != null) {
      return mbeanName;
    }
    return super.newMBeanName();
  }

  /**
   * The default implementation of ConfigurableWroFilter should allow setting of pre & post processors in configuration
   * properties. This will work only if no custom {@link WroManagerFactory} is configured.
   */
  @Override
  protected WroManagerFactory newWroManagerFactory() {
    return new DefaultWroManagerFactory(properties);
  }

  /**
   * @param mbeanName
   *          the mbeanName to set
   */
  public void setMbeanName(final String mbeanName) {
    this.mbeanName = mbeanName;
  }

  /**
   * @param jmxEnabled
   *          the jmxEnabled to set
   */
  public void setJmxEnabled(final boolean jmxEnabled) {
    this.jmxEnabled = jmxEnabled;
  }

  /**
   * @param debug
   *          the debug to set
   */
  public final void setDebug(final boolean debug) {
    this.debug = debug;
  }

  /**
   * @param gzipEnabled
   *          the gzipEnabled to set
   */
  public final void setGzipEnabled(final boolean gzipEnabled) {
    this.gzipEnabled = gzipEnabled;
  }

  /**
   * @param cacheUpdatePeriod
   *          the cacheUpdatePeriod to set
   */
  public final void setCacheUpdatePeriod(final long cacheUpdatePeriod) {
    this.cacheUpdatePeriod = cacheUpdatePeriod;
  }

  /**
   * @param modelUpdatePeriod
   *          the modelUpdatePeriod to set
   */
  public final void setModelUpdatePeriod(final long modelUpdatePeriod) {
    this.modelUpdatePeriod = modelUpdatePeriod;
  }

  /**
   * @param properties
   *          the properties to set
   */
  public void setProperties(final Properties properties) {
    this.properties = properties;
  }

  /**
   * @return the encoding
   */
  public String getEncoding() {
    return this.encoding;
  }

  /**
   * @param encoding
   *          the encoding to set
   */
  public void setEncoding(final String encoding) {
    this.encoding = encoding;
  }
}


/*
 * Copyright (C) 2011.
 * All rights reserved.
 */
package ro.isdc.wro.model.factory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ro.isdc.wro.WroRuntimeException;
import ro.isdc.wro.model.WroModel;

/**
 * An {@link XmlModelFactory} that handles the situation when the wro model cannot be loaded at some point (resource
 * cannot be located or the model is invalid). It holds the last known good model and reuse it until a new valid
 * instance of model is available.
 *
 * @author Alex Objelean
 * @created 13 Mar 2011
 */
public class FallbackAwareWroModelFactory extends WroModelFactoryDecorator {
  /**
   * Logger.
   */
  private static final Logger LOG = LoggerFactory.getLogger(FallbackAwareWroModelFactory.class);
  /**
   * Last valid model instance..
   */
  private WroModel lastValidModel;

  /**
   * @param decorated {@link WroModelFactory} to decorated.
   */
  public FallbackAwareWroModelFactory(final WroModelFactory decorated) {
    super(decorated);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public WroModel create() {
    WroModel newModel = null;
    try {
      newModel = super.create();
    } catch (final WroRuntimeException e) {
      LOG.error("Error while creating the model", e);
    }
    if (newModel == null) {
      LOG.warn("Couldn't load new model, reusing last Valid Model!");
      if (lastValidModel == null) {
        throw new WroRuntimeException("No valid model was found!");
      }
      return lastValidModel;
    }
    lastValidModel = newModel;
    return lastValidModel;
  }
}


package ro.isdc.wro.model.resource.processor.decorator;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ro.isdc.wro.WroRuntimeException;
import ro.isdc.wro.config.ReadOnlyContext;
import ro.isdc.wro.config.jmx.WroConfiguration;
import ro.isdc.wro.model.group.Inject;
import ro.isdc.wro.model.resource.Resource;

/**
 * Responsible for handling exception thrown by decorated processor. If the processing fails, the behavior will vary based on the {@link WroConfiguration#isIgnoreFailingProcessor()} flag:
 *

 *
When the flag is false (default) - the exception is wrapped in {@link WroRuntimeException} and thrown further

 *
When the flag is true - the exception is ignored and the writer will get the unchanged content from the reader.

 *

 *
 * @author Alex Objelean
 * @created 23 May 2012
 * @since 1.4.7
 */
public class ExceptionHandlingProcessorDecorator
    extends ProcessorDecorator {
  private static final Logger LOG = LoggerFactory.getLogger(ExceptionHandlingProcessorDecorator.class);
  @Inject
  private ReadOnlyContext context;

  /**
   * Decorates a processor with failure handling ability.
   *
   * @param processor
   *          to decorate.
   */
  public ExceptionHandlingProcessorDecorator(final Object processor) {
    super(processor);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void process(final Resource resource, final Reader reader, final Writer writer)
      throws IOException {
    final String resourceContent = IOUtils.toString(reader);
    final Reader innerReader = new StringReader(resourceContent);
    final StringWriter innerWriter = new StringWriter();
    try {
      super.process(resource, innerReader, innerWriter);
      writer.write(innerWriter.toString());
    } catch (final Exception e) {
      final String processorName = toString();
      if (isIgnoreFailingProcessor()) {
        LOG.debug("Ignoring failed processor. Original Exception", e);
        writer.write(resourceContent);
        // don't wrap exception unless required
      } else {
        LOG.error("Failed to process the resource: {} using processor: {}. Reason: {}", resource, processorName, e.getMessage());
        final String resourceUri = resource != null ? resource.getUri() : null;
        throw WroRuntimeException.wrap(e,
            "The processor: " + processorName + " faile while processing uri: " + resourceUri).setResource(resource);
      }
    } finally {
      reader.close();
      writer.close();
    }
  }

  /**
   * @return true if the failure should be ignored. By default uses the {@link WroConfiguration} to get the flag value.
   */
  protected boolean isIgnoreFailingProcessor() {
    return context.getConfig().isIgnoreFailingProcessor();
  }
}


/**
 * Copyright Alex Objelean
 */
package ro.isdc.wro.model.group.processor;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.Validate;

import ro.isdc.wro.model.group.GroupExtractor;
import ro.isdc.wro.model.resource.ResourceType;

/**
 * A simple decorator for GroupsProcessor.
 *
 * @author Alex Objelean
 */
public class GroupExtractorDecorator
  implements GroupExtractor {
  /**
   * {@link GroupExtractor} to decorate.
   */
  private final GroupExtractor decorated;

  /**
   * Decorates a {@link GroupExtractor} implementation.
   *
   * @param decorated {@link GroupExtractor} to decorate.
   */
  public GroupExtractorDecorator(final GroupExtractor decorated) {
    Validate.notNull(decorated);
    this.decorated = decorated;
  }

  /**
   * {@inheritDoc}
   */
  public String getGroupName(final HttpServletRequest request) {
    return decorated.getGroupName(request);
  }

  /**
   * {@inheritDoc}
   */
  public ResourceType getResourceType(final HttpServletRequest request) {
    return decorated.getResourceType(request);
  }

  /**
   * {@inheritDoc}
   */
  public boolean isMinimized(final HttpServletRequest request) {
    return decorated.isMinimized(request);
  }

  /**
   * {@inheritDoc}
   */
  public String encodeGroupUrl(final String groupName, final ResourceType resourceType, final boolean minimize) {
    return decorated.encodeGroupUrl(groupName, resourceType, minimize);
  }
}


/**
 * Copyright wro4j@2011
 */
package ro.isdc.wro.config.jmx;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.CharEncoding;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ro.isdc.wro.manager.factory.WroManagerFactory;

/**
 * Defines MBean which manage configuration. There should be only one instance of this object in the application and it
 * should be accessible even outside of the request cycle.
 *
 * @author Alex Objelean
 */
public class WroConfiguration
    implements WroConfigurationMBean {
  private static final Logger LOG = LoggerFactory.getLogger(WroConfiguration.class);
  /**
   * Default encoding to use.
   */
  public static final String DEFAULT_ENCODING = CharEncoding.UTF_8;
  /**
   * Default value for connectionTimeout property.
   */
  public static final int DEFAULT_CONNECTION_TIMEOUT = 2000;
  /**
   * How often to run a thread responsible for refreshing the cache.
   */
  private long cacheUpdatePeriod;
  /**
   * How often to run a thread responsible for refreshing the model.
   */
  private long modelUpdatePeriod;
  /**
   * How often to run a thread responsible for detecting resource changes.
   */
  private long resourceWatcherUpdatePeriod;
  /**
   * Flag for enabling an experimental feature which allows asynchronous resource watcher check.
   */
  private boolean resourceWatcherAsync;
  /**
   * Gzip enable flag.
   */
  private boolean gzipEnabled = true;
  /**
   * If true, we are running in DEVELOPMENT mode. By default this value is true.
   */
  private boolean debug = true;
  /**
   * If true, missing resources are ignored. By default this value is true.
   */
  private boolean ignoreMissingResources = true;

  /**
   * When this flag is enabled, the raw processed content will be gzipped only the first time and all subsequent
   * requests will use the cached gzipped content. Otherwise, the gzip operation will be performed for each request.
   * This flag allow to control the memory vs processing power trade-off.
   */
  private boolean cacheGzippedContent = false;
  /**
   * Allow to turn jmx on or off. By default this value is true.
   */
  private boolean jmxEnabled = true;
  /**
   * Fully qualified class name of the {@link WroManagerFactory} implementation.
   */
  private String wroManagerClassName;
  /**
   * Encoding to use when reading resources.
   */
  private String encoding = DEFAULT_ENCODING;
  /**
   * A preferred name of the MBean object.
   */
  private String mbeanName;
  /**
   * The parameter used to specify headers to put into the response, used mainly for caching.
   */
  private String header;
  /**
   * Timeout (milliseconds) of the url connection for external resources. This is used to ensure that locator doesn't
   * spend too much time on slow end-point.
   */
  private int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;
  /**
   * When true, will run in parallel preprocessing of multiple resources. In theory this should improve the performance.
   * By default this flag is false, because this feature is experimental.
   */
  private boolean parallelPreprocessing = false;
  /**
   * When a group is empty and this flag is false, the processing will fail. This is useful for runtime solution to
   * allow filter chaining when there is nothing to process for a given request.
   */
  private boolean ignoreEmptyGroup = true;
  /**
   * When this flag is true, any failure during processor, will leave the content unchanged. Otherwise, the exception
   * will interrupt processing with a {@link RuntimeException}.
   */
  private boolean ignoreFailingProcessor = false;
  /**
   * When this flag is false, the minimization will be suppressed for all resources. This flag is enabled by default.
   */
  private boolean minimizeEnabled = true;
  /**
   * Listeners for the change of cache & model period properties.
   */
  private final transient List<PropertyChangeListener> cacheUpdatePeriodListeners = new ArrayList<PropertyChangeListener>(
      1);
  private final transient List<PropertyChangeListener> modelUpdatePeriodListeners = new ArrayList<PropertyChangeListener>(
      1);

  /**
   * @return the name of the object used to register the MBean.
   */
  public static String getObjectName() {
    return WroConfiguration.class.getPackage().getName() + ".jmx:type=" + WroConfiguration.class.getSimpleName();
  }

  /**
   * {@inheritDoc}
   */
  public long getCacheUpdatePeriod() {
    return this.cacheUpdatePeriod;
  }

  /**
   * {@inheritDoc}Async
   */
  public long getModelUpdatePeriod() {
    return modelUpdatePeriod;
  }

  /**
   * {@inheritDoc}
   */
  public void setCacheUpdatePeriod(final long period) {
    if (period != cacheUpdatePeriod) {
      reloadCacheWithNewValue(period);
    }
    this.cacheUpdatePeriod = period;
  }

  /**
   * {@inheritDoc}
   */
  public void setModelUpdatePeriod(final long period) {
    if (period != modelUpdatePeriod) {
      reloadModelWithNewValue(period);
    }
    this.modelUpdatePeriod = period;
  }

  /**
   * {@inheritDoc}
   */
  public boolean isGzipEnabled() {
    return gzipEnabled;
  }

  /**
   * {@inheritDoc}
   */
  public void setGzipEnabled(final boolean enable) {
    gzipEnabled = enable;
  }

  /**
   * {@inheritDoc}
   */
  public void reloadCache() {
    reloadCacheWithNewValue(null);
  }

  /**
   * Notify all listeners about cachePeriod property changed. If passed newValue is null, the oldValue is taken as new
   * value. This is the case when the reloadCache is invoked.
   *
   * @param newValue
   *          value to set.
   */
  private void reloadCacheWithNewValue(final Long newValue) {
    final long newValueAsPrimitive = newValue == null ? getCacheUpdatePeriod() : newValue;
    LOG.debug("invoking {} listeners", cacheUpdatePeriodListeners.size());
    for (final PropertyChangeListener listener : cacheUpdatePeriodListeners) {
      final PropertyChangeEvent event = new PropertyChangeEvent(this, "cache", getCacheUpdatePeriod(),
          newValueAsPrimitive);
      listener.propertyChange(event);
    }
  }

  /**
   * {@inheritDoc}
   */
  public void reloadModel() {
    LOG.debug("reloadModel");
    reloadModelWithNewValue(null);
  }

  /**
   * Notify all listeners about cachePeriod property changed. If passed newValue is null, the oldValue is taken as new
   * value. This is the case when the reloadModel is invoked.
   *
   * @param newValue
   *          value to set.
   */
  private void reloadModelWithNewValue(final Long newValue) {
    final long newValueAsPrimitive = newValue == null ? getModelUpdatePeriod() : newValue;
    for (final PropertyChangeListener listener : modelUpdatePeriodListeners) {
      final PropertyChangeEvent event = new PropertyChangeEvent(this, "model", getModelUpdatePeriod(),
          newValueAsPrimitive);
      listener.propertyChange(event);
    }
  }

  /**
   * Register a listener which is notified when the modelUpdate period value is changed. Registration is allowed only
   * during
   *
   * @param listener
   *          to add.
   */
  public void registerModelUpdatePeriodChangeListener(final PropertyChangeListener listener) {
    modelUpdatePeriodListeners.add(listener);
  }

  /**
   * Register a listener which is notified when the modelUpdate period value is changed.
   *
   * @param listener
   *          to add.
   */
  public void registerCacheUpdatePeriodChangeListener(final PropertyChangeListener listener) {
    cacheUpdatePeriodListeners.add(listener);
  }

  /**
   * @return the debug
   */
  public boolean isDebug() {
    return this.debug;
  }

  /**
   * @param debug
   *          the debug to set
   */
  public void setDebug(final boolean debug) {
    // Don't think that we really need to reload the cache here
    this.debug = debug;
  }

  /**
   * @return the ignoreMissingResources
   */
  public boolean isIgnoreMissingResources() {
    return this.ignoreMissingResources;
  }

  /**
   * @param ignoreMissingResources
   *          the ignoreMissingResources to set
   */
  public void setIgnoreMissingResources(final boolean ignoreMissingResources) {
    this.ignoreMissingResources = ignoreMissingResources;
  }

  /**
   * @return the jmxEnabled
   */
  public boolean isJmxEnabled() {
    return jmxEnabled;
  }

  /**
   * @param jmxEnabled
   *          the jmxEnabled to set
   */
  public void setJmxEnabled(final boolean jmxEnabled) {
    this.jmxEnabled = jmxEnabled;
  }

  /**
   * @return the cacheGzippedContent
   */
  public boolean isCacheGzippedContent() {
    return this.cacheGzippedContent;
  }

  /**
   * @param cacheGzippedContent
   *          the cacheGzippedContent to set
   */
  public void setCacheGzippedContent(final boolean cacheGzippedContent) {
    this.cacheGzippedContent = cacheGzippedContent;
  }

  /**
   * Perform the cleanup, clear the listeners.
   */
  public void destroy() {
    cacheUpdatePeriodListeners.clear();
    modelUpdatePeriodListeners.clear();
  }

  /**
   * @return the encoding
   */
  public String getEncoding() {
    return this.encoding;
  }

  /**
   * @param encoding
   *          the encoding to set
   */
  public void setEncoding(final String encoding) {
    this.encoding = encoding == null ? DEFAULT_ENCODING : encoding;
  }

  /**
   * @return the wroManagerClassName
   */
  public String getWroManagerClassName() {
    return this.wroManagerClassName;
  }

  /**
   * @param wroManagerClassName
   *          the wroManagerClassName to set
   */
  public void setWroManagerClassName(final String wroManagerClassName) {
    this.wroManagerClassName = wroManagerClassName;
  }

  /**
   * @return the mbeanName
   */
  public String getMbeanName() {
    return this.mbeanName;
  }

  /**
   * @param mbeanName
   *          the mbeanName to set
   */
  public void setMbeanName(final String mbeanName) {
    this.mbeanName = mbeanName;
  }

  /**
   * @return the header
   */
  public String getHeader() {
    return this.header;
  }

  /**
   * @param header
   *          the header to set
   */
  public void setHeader(final String header) {
    this.header = header;
  }

  /**
   * @return the number of milliseconds before a connection is timed out.
   */
  public int getConnectionTimeout() {
    return this.connectionTimeout;
  }

  /**
   * Timeout (milliseconds) of the url connection for external resources. This is used to ensure that locator doesn't
   * spend too much time on slow end-point.
   *
   * @param connectionTimeout
   *          the connectionTimeout to set
   */
  public void setConnectionTimeout(final int connectionTimeout) {
    this.connectionTimeout = connectionTimeout;
  }

  /**
   * @return the parallelPreprocessing
   */
  public boolean isParallelPreprocessing() {
    return this.parallelPreprocessing;
  }

  /**
   * @param parallelPreprocessing
   *          the parallelPreprocessing to set
   */
  public void setParallelPreprocessing(final boolean parallelPreprocessing) {
    this.parallelPreprocessing = parallelPreprocessing;
  }

  /**
   * @return value of the flag responsible for handling empty group behavior.
   */
  public boolean isIgnoreEmptyGroup() {
    return ignoreEmptyGroup;
  }

  /**
   * @param ignoreEmptyGroup
   *          flag for turning on/off failure when there is an empty group (nothing to process). This value is true by
   *          default, meaning that empty group will produce empty result (no exception).
   */
  public void setIgnoreEmptyGroup(final boolean ignoreEmptyGroup) {
    this.ignoreEmptyGroup = ignoreEmptyGroup;
  }

  /**
   * @return true if the processing failure should be ignored.
   */
  public boolean isIgnoreFailingProcessor() {
    return ignoreFailingProcessor;
  }

  public void setIgnoreFailingProcessor(final boolean ignoreFailingProcessor) {
    this.ignoreFailingProcessor = ignoreFailingProcessor;
  }

  public final long getResourceWatcherUpdatePeriod() {
    return resourceWatcherUpdatePeriod;
  }

  public final void setResourceWatcherUpdatePeriod(final long resourceWatcherUpdatePeriod) {
    this.resourceWatcherUpdatePeriod = resourceWatcherUpdatePeriod;
  }

  /**
   * @return flag indicating if the minimization is enabled. When this flag is false, the minimization will be
   *         suppressed for all resources.
   */
  public boolean isMinimizeEnabled() {
    return minimizeEnabled;
  }

  public void setMinimizeEnabled(final boolean minimizeEnabled) {
    this.minimizeEnabled = minimizeEnabled;
  }

  /**
   * @return true if the asynchronous resourceWatcher experimental feature is enabled.
   */
  public boolean isResourceWatcherAsync() {
    return resourceWatcherAsync;
  }

  public void setResourceWatcherAsync(final boolean resourceWatcherAsync) {
    this.resourceWatcherAsync = resourceWatcherAsync;
  }

  @Override
  public boolean equals(final Object obj) {
    return EqualsBuilder.reflectionEquals(this, obj, true);
  }

  @Override
  public int hashCode() {
    return HashCodeBuilder.reflectionHashCode(this);
  }

  @Override
  public String toString() {
    return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE).toString();
  }
}


package ro.isdc.wro.extensions.processor.support;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import ro.isdc.wro.extensions.processor.css.BourbonCssProcessor;
import ro.isdc.wro.extensions.processor.css.CssLintProcessor;
import ro.isdc.wro.extensions.processor.css.Less4jProcessor;
import ro.isdc.wro.extensions.processor.css.LessCssProcessor;
import ro.isdc.wro.extensions.processor.css.NodeLessCssProcessor;
import ro.isdc.wro.extensions.processor.css.RhinoLessCssProcessor;
import ro.isdc.wro.extensions.processor.css.RubySassCssProcessor;
import ro.isdc.wro.extensions.processor.css.SassCssProcessor;
import ro.isdc.wro.extensions.processor.css.YUICssCompressorProcessor;
import ro.isdc.wro.extensions.processor.js.BeautifyJsProcessor;
import ro.isdc.wro.extensions.processor.js.CJsonProcessor;
import ro.isdc.wro.extensions.processor.js.CoffeeScriptProcessor;
import ro.isdc.wro.extensions.processor.js.DojoShrinksafeCompressorProcessor;
import ro.isdc.wro.extensions.processor.js.DustJsProcessor;
import ro.isdc.wro.extensions.processor.js.EmberJsProcessor;
import ro.isdc.wro.extensions.processor.js.GoogleClosureCompressorProcessor;
import ro.isdc.wro.extensions.processor.js.HandlebarsJsProcessor;
import ro.isdc.wro.extensions.processor.js.HoganJsProcessor;
import ro.isdc.wro.extensions.processor.js.JsHintProcessor;
import ro.isdc.wro.extensions.processor.js.JsLintProcessor;
import ro.isdc.wro.extensions.processor.js.JsonHPackProcessor;
import ro.isdc.wro.extensions.processor.js.LiterateCoffeeScriptProcessor;
import ro.isdc.wro.extensions.processor.js.NgAnnotateProcessor;
import ro.isdc.wro.extensions.processor.js.NgMinProcessor;
import ro.isdc.wro.extensions.processor.js.NodeCoffeeScriptProcessor;
import ro.isdc.wro.extensions.processor.js.NodeLiterateCoffeeScriptProcessor;
import ro.isdc.wro.extensions.processor.js.NodeTypeScriptProcessor;
import ro.isdc.wro.extensions.processor.js.PackerJsProcessor;
import ro.isdc.wro.extensions.processor.js.RhinoCoffeeScriptProcessor;
import ro.isdc.wro.extensions.processor.js.RhinoLiterateCoffeeScriptProcessor;
import ro.isdc.wro.extensions.processor.js.RhinoTypeScriptProcessor;
import ro.isdc.wro.extensions.processor.js.TypeScriptProcessor;
import ro.isdc.wro.extensions.processor.js.UglifyJsProcessor;
import ro.isdc.wro.model.resource.processor.ResourcePostProcessor;
import ro.isdc.wro.model.resource.processor.ResourcePreProcessor;
import ro.isdc.wro.model.resource.processor.decorator.LazyProcessorDecorator;
import ro.isdc.wro.model.resource.processor.decorator.ProcessorDecorator;
import ro.isdc.wro.model.resource.processor.support.ProcessorProvider;
import ro.isdc.wro.util.LazyInitializer;

import com.google.javascript.jscomp.CompilationLevel;

/**
 * The implementation which contributes with processors from core module.
 *
 * @author Alex Objelean
 * @auther Thilo Planz
 * @created 1 Jun 2012
 */
public class DefaultProcessorProvider
    implements ProcessorProvider {
  /**
   * {@inheritDoc}
   */
  @Override
  public Map<String, ResourcePreProcessor> providePreProcessors() {
    return createMap();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Map<String, ResourcePostProcessor> providePostProcessors() {
    final Map<String, ResourcePostProcessor> resultMap = new HashMap<String, ResourcePostProcessor>();
    /**
     * Created to overcome the difference between {@link ResourcePreProcessor} and {@link ResourcePostProcessor}
     * interfaces which will be resolved in next major version.
     */
    final Map<String, ResourcePreProcessor> preProcessorsMap = createMap();
    for (final Entry<String, ResourcePreProcessor> entry : preProcessorsMap.entrySet()) {
      resultMap.put(entry.getKey(), new ProcessorDecorator(entry.getValue()));
    }
    return resultMap;
  }

  /**
   * @return the map of pre processors.
   */
  private Map<String, ResourcePreProcessor> createMap() {
    final Map<String, ResourcePreProcessor> map = new HashMap<String, ResourcePreProcessor>();
    map.put(YUICssCompressorProcessor.ALIAS, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
      @Override
      protected ResourcePreProcessor initialize() {
        return new YUICssCompressorProcessor();
      }
    }));
    map.put(DojoShrinksafeCompressorProcessor.ALIAS, new LazyProcessorDecorator(
        new LazyInitializer<ResourcePreProcessor>() {
          @Override
          protected ResourcePreProcessor initialize() {
            return new DojoShrinksafeCompressorProcessor();
          }
        }));
    map.put(UglifyJsProcessor.ALIAS_UGLIFY, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
      @Override
      protected ResourcePreProcessor initialize() {
        return new UglifyJsProcessor();
      }
    }));
    map.put(BeautifyJsProcessor.ALIAS_BEAUTIFY, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
      @Override
      protected ResourcePreProcessor initialize() {
        return new BeautifyJsProcessor();
      }
    }));
    map.put(PackerJsProcessor.ALIAS, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
      @Override
      protected ResourcePreProcessor initialize() {
        return new PackerJsProcessor();
      }
    }));
    map.put(RhinoLessCssProcessor.ALIAS, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
      @Override
      protected ResourcePreProcessor initialize() {
        return new RhinoLessCssProcessor();
      }
    }));
    map.put(NodeLessCssProcessor.ALIAS, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
      @Override
      protected ResourcePreProcessor initialize() {
        return new NodeLessCssProcessor();
      }
    }));
    map.put(Less4jProcessor.ALIAS, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
      @Override
      protected ResourcePreProcessor initialize() {
        return new Less4jProcessor();
      }
    }));
    map.put(LessCssProcessor.ALIAS, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
      @Override
      protected ResourcePreProcessor initialize() {
        return new LessCssProcessor();
      }
    }));
    map.put(SassCssProcessor.ALIAS, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
      @Override
      protected ResourcePreProcessor initialize() {
        return new SassCssProcessor();
      }
    }));
    map.put(RubySassCssProcessor.ALIAS, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
      @Override
      protected ResourcePreProcessor initialize() {
        return new RubySassCssProcessor();
      }
    }));
    map.put(BourbonCssProcessor.ALIAS, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
      @Override
      protected ResourcePreProcessor initialize() {
        return new BourbonCssProcessor();
      }
    }));
    map.put(GoogleClosureCompressorProcessor.ALIAS_SIMPLE, new LazyProcessorDecorator(
        new LazyInitializer<ResourcePreProcessor>() {
          @Override
          protected ResourcePreProcessor initialize() {
            return new GoogleClosureCompressorProcessor(CompilationLevel.SIMPLE_OPTIMIZATIONS);
          }
        }));
    map.put(GoogleClosureCompressorProcessor.ALIAS_ADVANCED, new LazyProcessorDecorator(
        new LazyInitializer<ResourcePreProcessor>() {
          @Override
          protected ResourcePreProcessor initialize() {
            return new GoogleClosureCompressorProcessor(CompilationLevel.ADVANCED_OPTIMIZATIONS);
          }
        }));
    map.put(GoogleClosureCompressorProcessor.ALIAS_WHITESPACE_ONLY, new LazyProcessorDecorator(
        new LazyInitializer<ResourcePreProcessor>() {
          @Override
          protected ResourcePreProcessor initialize() {
            return new GoogleClosureCompressorProcessor(CompilationLevel.WHITESPACE_ONLY);
          }
        }));
    map.put(RhinoCoffeeScriptProcessor.ALIAS, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
      @Override
      protected ResourcePreProcessor initialize() {
        return new RhinoCoffeeScriptProcessor();
      }
    }));
    map.put(NodeCoffeeScriptProcessor.ALIAS, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
      @Override
      protected ResourcePreProcessor initialize() {
        return new NodeCoffeeScriptProcessor();
      }
    }));
    map.put(CoffeeScriptProcessor.ALIAS, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
      @Override
      protected ResourcePreProcessor initialize() {
        return new CoffeeScriptProcessor();
      }
    }));
    map.put(RhinoLiterateCoffeeScriptProcessor.ALIAS, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
        @Override
        protected ResourcePreProcessor initialize() {
          return new RhinoLiterateCoffeeScriptProcessor();
        }
      }));
    map.put(NodeLiterateCoffeeScriptProcessor.ALIAS, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
        @Override
        protected ResourcePreProcessor initialize() {
          return new NodeLiterateCoffeeScriptProcessor();
        }
      }));
    map.put(LiterateCoffeeScriptProcessor.ALIAS, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
        @Override
        protected ResourcePreProcessor initialize() {
          return new LiterateCoffeeScriptProcessor();
        }
      }));
    map.put(CJsonProcessor.ALIAS_PACK, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
      @Override
      protected ResourcePreProcessor initialize() {
        return CJsonProcessor.packProcessor();
      }
    }));
    map.put(CJsonProcessor.ALIAS_UNPACK, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
      @Override
      protected ResourcePreProcessor initialize() {
        return CJsonProcessor.unpackProcessor();
      }
    }));
    map.put(JsonHPackProcessor.ALIAS_PACK, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
      @Override
      protected ResourcePreProcessor initialize() {
        return JsonHPackProcessor.packProcessor();
      }
    }));
    map.put(JsonHPackProcessor.ALIAS_UNPACK, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
      @Override
      protected ResourcePreProcessor initialize() {
        return JsonHPackProcessor.unpackProcessor();
      }
    }));
    map.put(JsHintProcessor.ALIAS, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
      @Override
      protected ResourcePreProcessor initialize() {
        return new JsHintProcessor();
      }
    }));
    map.put(JsLintProcessor.ALIAS, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
      @Override
      protected ResourcePreProcessor initialize() {
        return new JsLintProcessor();
      }
    }));
    map.put(CssLintProcessor.ALIAS, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
      @Override
      protected ResourcePreProcessor initialize() {
        return new CssLintProcessor();
      }
    }));
    map.put(DustJsProcessor.ALIAS, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
      @Override
      protected ResourcePreProcessor initialize() {
        return new DustJsProcessor();
      }
    }));
    map.put(HoganJsProcessor.ALIAS, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
      @Override
      protected ResourcePreProcessor initialize() {
        return new HoganJsProcessor();
      }
    }));
    map.put(HandlebarsJsProcessor.ALIAS, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
      @Override
      protected ResourcePreProcessor initialize() {
        return new HandlebarsJsProcessor();
      }
    }));
    map.put(RhinoTypeScriptProcessor.ALIAS, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
      @Override
      protected ResourcePreProcessor initialize() {
        return new RhinoTypeScriptProcessor();
      }
    }));
    map.put(TypeScriptProcessor.ALIAS, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
      @Override
      protected ResourcePreProcessor initialize() {
        return new TypeScriptProcessor();
      }
    }));
    map.put(NodeTypeScriptProcessor.ALIAS, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
      @Override
      protected ResourcePreProcessor initialize() {
        return new NodeTypeScriptProcessor();
      }
    }));
    map.put(EmberJsProcessor.ALIAS, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
      @Override
      protected ResourcePreProcessor initialize() {
        return new EmberJsProcessor();
      }
    }));
    map.put(NgMinProcessor.ALIAS, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
        @Override
        protected ResourcePreProcessor initialize() {
          return new NgMinProcessor();
        }
      }));
    map.put(NgAnnotateProcessor.ALIAS, new LazyProcessorDecorator(new LazyInitializer<ResourcePreProcessor>() {
        @Override
        protected ResourcePreProcessor initialize() {
          return new NgAnnotateProcessor();
        }
      }));
    return map;
  }
}


/*
 * Copyright (C) 2011. All rights reserved.
 */
package ro.isdc.wro.util;

import static org.apache.commons.lang3.Validate.notNull;

import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Encapsulates the logic which handles scheduler creation and destroy. This class is thread-safe.
 *
 * @author Alex Objelean
 * @created 14 Oct 2011
 * @since 1.4.2
 */
public class SchedulerHelper {
  private static final Logger LOG = LoggerFactory.getLogger(SchedulerHelper.class);

  private final LazyInitializer<ScheduledThreadPoolExecutor> poolInitializer = new LazyInitializer<ScheduledThreadPoolExecutor>() {
    @Override
    protected ScheduledThreadPoolExecutor initialize() {
      return new ScheduledThreadPoolExecutor(1, WroUtil.createDaemonThreadFactory(SchedulerHelper.this.name)) {
        @Override
        public boolean getExecuteExistingDelayedTasksAfterShutdownPolicy() {
          return false;
        };
      };
    }
  };

  /**
   * An initializer providing the runnable to schedule.
   */
  private final LazyInitializer<Runnable> lazyRunnable;
  /**
   * Period in seconds (how often a runnable should run).
   */
  private volatile long period = 0;
  /**
   * The name of this scheduler (used for logging).
   */
  private final String name;
  /**
   * The future of the currently running task. Allows reschedule operation by canceling execution of the running
   * thread.
   */
  private ScheduledFuture<?> future;

  private SchedulerHelper(final LazyInitializer<Runnable> lazyRunnable) {
    this(lazyRunnable, null);
  }

  private SchedulerHelper(final LazyInitializer<Runnable> lazyRunnable, final String name) {
    notNull(lazyRunnable);
    this.name = name;
    this.lazyRunnable = lazyRunnable;
  }

  /**
   * Factory method. Creates a {@link SchedulerHelper} which consumes a factory providing a runnable. This approach
   * allows lazy runnable initialization.
   *
   * @param runnableFactory
   *          a factory creating the runnable to schedule.
   * @param name
   *          the name associated with this {@link SchedulerHelper} (useful to detect if this class is causing a memory
   *          leak.
   */
  public static SchedulerHelper create(final LazyInitializer<Runnable> runnableFactory, final String name) {
    return new SchedulerHelper(runnableFactory, name);
  }

  /**
   * @see SchedulerHelper#create(ObjectFactory, String)
   */
  public static SchedulerHelper create(final LazyInitializer<Runnable> runnableFactory) {
    return new SchedulerHelper(runnableFactory);
  }

  /**
   * Run the scheduler with the provided period of time. If the scheduler is already started, it will be stopped (not
   * before the running job is complete).
   *
   * @param period
   *          new period for scheduling.
   * @param timeUnit
   *          what kind of time unit is associated with the period.
   */
  public SchedulerHelper scheduleWithPeriod(final long period, final TimeUnit timeUnit) {
    notNull(timeUnit);
    LOG.debug("period: {} [{}]", period, timeUnit);
    if (this.period != period) {
      this.period = period;
      if (!poolInitializer.get().isShutdown()) {
        startScheduler(period, timeUnit);
      } else {
        LOG.warn("Cannot schedule because destroy was already called!");
      }
    }
    return this;
  }

  private synchronized void startScheduler(final long period, final TimeUnit timeUnit) {
    // let the running task to finish its job.
    cancelRunningTask();
    if (period > 0) {
      // scheduleWithFixedDelay is used instead of scheduleAtFixedRate because the later can cause a problem
      // (thread tries to make up for lost time in some situations)
      final Runnable runnable = lazyRunnable.get();
      notNull(runnable);
      // avoid reject when this method is accessed concurrently.
      if (!poolInitializer.get().isShutdown()) {
        LOG.debug("[START] Scheduling thread with period of {} {}. ThreadId:  {}", period, timeUnit,
            Thread.currentThread().getId());
        // do not execute immediately. Use period also for initial delay.
        final long initialDelay = period;
        future = poolInitializer.get().scheduleWithFixedDelay(runnable, initialDelay, period, timeUnit);
      }
    }
  }

  /**
   * The following method shuts down an ExecutorService in two phases, first by calling shutdown to reject incoming
   * tasks, and then calling shutdownNow, if necessary, to cancel any lingering tasks:
   *
   * @param destroyNow
   *          - if true, any running operation will be stopped immediately, otherwise scheduler will await termination.
   */
  private synchronized void destroyScheduler() {
    if (!poolInitializer.get().isShutdown()) {
      // Disable new tasks from being submitted
      poolInitializer.get().shutdown();
      if (future != null) {
        future.cancel(true);
      }
      try {
        while (!poolInitializer.get().awaitTermination(5, TimeUnit.SECONDS)) {
          LOG.debug("Termination awaited: {}", name);
          poolInitializer.get().shutdownNow();
        }
      } catch (final InterruptedException e) {
        LOG.debug("Interrupted Exception occured during scheduler destroy", e);
        // (Re-)Cancel if current thread also interrupted
        poolInitializer.get().shutdownNow();
        // Preserve interrupt status
        Thread.currentThread().interrupt();
      } finally {
        LOG.debug("[STOP] Scheduler terminated successfully! {}", name);
      }
    }
  }

  private void cancelRunningTask() {
    if (future != null) {
      future.cancel(false);
      LOG.debug("[STOP] Scheduler terminated successfully! {}", name);
    }
  }

  /**
   * Schedules with provided period using {@link TimeUnit#SECONDS} as a default time unit.
   *
   * @param period
   *          new period for scheduling.
   */
  public SchedulerHelper scheduleWithPeriod(final long period) {
    scheduleWithPeriod(period, TimeUnit.SECONDS);
    return this;
  }

  /**
   * Stops all jobs runned by the scheduler. It is important to call this method before application stops.
   */
  public void destroy() {
    destroyScheduler();
  }

  /**
   * @return the period
   */
  long getPeriod() {
    return period;
  }
}


/*
 * Copyright (c) 2008. All rights reserved.
 */
package ro.isdc.wro.model.resource.locator.support;

import static org.apache.commons.lang3.Validate.notNull;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ro.isdc.wro.config.jmx.WroConfiguration;
import ro.isdc.wro.http.support.RedirectedStreamServletResponseWrapper;
import ro.isdc.wro.model.resource.locator.UriLocator;
import ro.isdc.wro.model.resource.locator.UrlUriLocator;
import ro.isdc.wro.util.WroUtil;

/**
 * Responsible to locate a context relative resource. Attempts to locate the resource using {@link RequestDispatcher} .
 * If the dispatcher fails, it will fallback resource retrieval to a http call using {@link UrlUriLocator}.
 *
 * @author Alex Objelean
 */
public class DispatcherStreamLocator {
  private static final Logger LOG = LoggerFactory.getLogger(DispatcherStreamLocator.class);
  /**
   * Attribute indicating that the request is included from within a wro request cycle. This is required to prevent
   * {@link StackOverflowError}.
   *
   * @VisibleForTesting
   */
  public static final String ATTRIBUTE_INCLUDED_BY_DISPATCHER = DispatcherStreamLocator.class.getName()
      + ".included_with_dispatcher";
  private int timeout = WroConfiguration.DEFAULT_CONNECTION_TIMEOUT;

  /**
   * /** When using JBoss Portal and it has some funny quirks...actually a portal application have several small web
   * application behind it. So when it intercepts a requests for portal then it start bombing the the application behind
   * the portal with multiple threads (web requests) that are combined with threads for wro4j.
   *
   * @param location
   *          a path to a request relative resource to locate.
   * @return a valid stream for required location. This method will never return a null.
   * @throws IOException
   *           if the stream cannot be located at the specified location.
   */
  public InputStream getInputStream(final HttpServletRequest request, final HttpServletResponse response,
      final String location)
      throws IOException {
    if (request == null || response == null || location == null) {
      throw new IOException("Cannot get stream for location: " + location
          + " because either request, response or location is not available");
    }

    // where to write the bytes of the stream
    final ByteArrayOutputStream os = new ByteArrayOutputStream();
    boolean warnOnEmptyStream = false;
    try {
      final RequestDispatcher dispatcher = request.getRequestDispatcher(location);
      if (dispatcher != null) {
        // Wrap request
        final ServletRequest servletRequest = getWrappedServletRequest(request, location);
        // Wrap response
        final ServletResponse servletResponse = new RedirectedStreamServletResponseWrapper(os, response);
        LOG.debug("dispatching request to location: {}", location);
        // use dispatcher
        dispatcher.include(servletRequest, servletResponse);
        warnOnEmptyStream = true;
        // force flushing - the content will be written to BytArrayOutputStream.
        // Otherwise exactly 32K of data will be written.
        servletResponse.getWriter().flush();
        os.close();
      }
    } catch (final Exception e) {
      LOG.debug("Could not dispatch request for location {}", location);
      // Not only servletException can be thrown, also dispatch.include can throw NPE when the scheduler runs outside
      // of the request cycle, thus connection is unavailable. This is caused mostly when invalid resources are
      // included.
      return locateExternal(request, location);
    }
    try {
      // fallback to external resource locator if the dispatcher is empty
      if (os.size() == 0) {
        return locateExternal(request, location);
      }
    } finally {
      if (warnOnEmptyStream && os.size() == 0) {
        LOG.debug("Wrong or empty resource with location: {}", location);
      }
    }
    return new ByteArrayInputStream(os.toByteArray());
  }

  public InputStream locateExternal(final HttpServletRequest request, final String location)
      throws IOException {
    final String servletContextPath = request.getRequestURL().toString().replace(request.getServletPath(), "");
    final String absolutePath = servletContextPath + location;
    LOG.debug("locateExternalUri: {}", absolutePath);
    return createExternalResourceLocator().locate(absolutePath);
  }

  /**
   * @return the {@link UriLocator} responsible for locating resources when dispatcher fails. No wildcard handling is
   *         required.
   * @VisibleForTesting
   */
  UriLocator createExternalResourceLocator() {
    final UrlUriLocator locator = new UrlUriLocator();
    locator.setEnableWildcards(false);
    locator.setTimeout(timeout);
    return locator;
  }

  /**
   * Build a wrapped servlet request which will be used for dispatching.
   */
  private ServletRequest getWrappedServletRequest(final HttpServletRequest request, final String location) {
    final HttpServletRequest wrappedRequest = new HttpServletRequestWrapper(request) {
      @Override
      public String getRequestURI() {
        return getContextPath() + location;
      }

      @Override
      public String getPathInfo() {
        return WroUtil.getPathInfoFromLocation(this, location);
      }

      @Override
      public String getServletPath() {
        return WroUtil.getServletPathFromLocation(this, location);
      }
    };
    // add an attribute to mark this request as included from wro
    wrappedRequest.setAttribute(ATTRIBUTE_INCLUDED_BY_DISPATCHER, Boolean.TRUE);
    return wrappedRequest;
  }

  /**
   * @return true if the request is included from within wro request cycle.
   */
  public static boolean isIncludedRequest(final HttpServletRequest request) {
    notNull(request);
    return request.getAttribute(ATTRIBUTE_INCLUDED_BY_DISPATCHER) != null;
  }

  /**
   * @param timeout
   *          time (in millis) used when an external url is requested for both: connection & read.
   */
  public void setTimeout(final int timeout) {
    this.timeout = timeout;
  }

  /**
   * @return the configured timeout for this instance of locator.
   */
  public int getTimeout() {
    return timeout;
  }
}