All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.matrixweb.smaller.osgi.http.Servlet Maven / Gradle / Ivy

package de.matrixweb.smaller.osgi.http;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.matrixweb.smaller.common.Manifest;
import de.matrixweb.smaller.common.SmallerException;
import de.matrixweb.smaller.common.Task;
import de.matrixweb.smaller.common.Task.GlobalOptions;
import de.matrixweb.smaller.common.Zip;
import de.matrixweb.smaller.pipeline.Pipeline;
import de.matrixweb.smaller.pipeline.Result;
import de.matrixweb.smaller.resource.FileResourceResolver;
import de.matrixweb.smaller.resource.ResourceResolver;
import de.matrixweb.smaller.resource.Type;

/**
 * @author marwol
 */
public class Servlet extends HttpServlet {

  private static final Logger LOGGER = LoggerFactory.getLogger(Servlet.class);

  private static final long serialVersionUID = -3500628755781284892L;

  private final Pipeline pipeline;

  /**
   * @param pipeline
   */
  public Servlet(final Pipeline pipeline) {
    super();
    this.pipeline = pipeline;
  }

  /**
   * @see javax.servlet.http.HttpServlet#service(javax.servlet.http.HttpServletRequest,
   *      javax.servlet.http.HttpServletResponse)
   */
  @Override
  protected void service(final HttpServletRequest request,
      final HttpServletResponse response) throws ServletException, IOException {
    LOGGER.info("Handle smaller request from {}", request.getRemoteAddr());
    final OutputStream out = response.getOutputStream();
    Context context = null;
    try {
      context = setUpContext(request.getInputStream());
      final ResourceResolver resolver = new FileResourceResolver(
          context.sourceDir.getAbsolutePath());
      Task task = context.manifest.getNext();
      while (task != null) {
        writeResults(this.pipeline.execute(resolver, task), context.targetDir,
            task);
        task = context.manifest.getNext();
      }
      response.setHeader("X-Smaller-Status", "OK");
      Zip.zip(out, context.targetDir);
    } catch (final SmallerException e) {
      final StringBuilder message = new StringBuilder(e.getMessage());
      Throwable t = e.getCause();
      while (t != null) {
        message.append(": ").append(t.getMessage());
        t = t.getCause();
      }
      response.setHeader("X-Smaller-Status", "ERROR");
      response.setHeader("X-Smaller-Message", message.toString());
    } catch (final IOException e) {
      LOGGER.error("Error during smaller execution", e);
    } finally {
      if (context != null) {
        context.inputZip.delete();
        FileUtils.deleteDirectory(context.sourceDir);
        FileUtils.deleteDirectory(context.targetDir);
      }
      IOUtils.closeQuietly(out);
    }
  }

  private Context setUpContext(final InputStream is) throws IOException {
    try {
      final Context context = unzip(is);
      final Manifest manifest = new ObjectMapper().readValue(
          getMainFile(context.sourceDir), Manifest.class);
      File output = context.sourceDir;
      if (GlobalOptions.isOutOnly(manifest.getTasks()[0])) {
        output = File.createTempFile("smaller-output", ".dir");
        output.delete();
        output.mkdirs();
      }
      context.targetDir = output;
      context.manifest = manifest;
      return context;
    } finally {
      IOUtils.closeQuietly(is);
    }
  }

  private Context unzip(final InputStream is) throws IOException {
    final Context context = storeZip(is);
    final File base = File.createTempFile("smaller-work", ".dir");
    base.delete();
    base.mkdir();
    Zip.unzip(context.inputZip, base);
    context.sourceDir = base;
    return context;
  }

  private Context storeZip(final InputStream in) throws IOException {
    final File temp = File.createTempFile("smaller-input", ".zip");
    temp.delete();
    FileOutputStream out = null;
    try {
      if (in.available() <= 0) {
        throw new IOException("Invalid attachment size; rejecting request");
      } else {
        out = new FileOutputStream(temp);
        IOUtils.copy(in, out);
      }
    } finally {
      IOUtils.closeQuietly(in);
      IOUtils.closeQuietly(out);
    }

    final Context context = new Context();
    context.inputZip = temp;
    return context;
  }

  private File getMainFile(final File input) {
    File main = new File(input, "META-INF/MAIN.json");
    if (!main.exists()) {
      // Old behaviour: Search directly in root of zip
      main = new File(input, "MAIN.json");
      if (!main.exists()) {
        throw new SmallerException(
            "Missing instructions file 'META-INF/MAIN.json'");
      }
    }
    return main;
  }

  private void writeResults(final Result result, final File outputDir,
      final Task task) throws IOException {
    writeResult(outputDir, task, result, Type.JS);
    writeResult(outputDir, task, result, Type.CSS);
  }

  private void writeResult(final File output, final Task task,
      final Result result, final Type type) throws IOException {
    final String outputFile = getTargetFile(output, task.getOut(), type);
    if (outputFile != null) {
      FileUtils.writeStringToFile(new File(outputFile), result.get(type)
          .getContents());
    }
  }

  private String getTargetFile(final File base, final String[] out,
      final Type type) {
    String target = null;
    for (final String s : out) {
      final String ext = FilenameUtils.getExtension(s);
      switch (type) {
      case JS:
        if (ext.equals("js")) {
          target = new File(base, s).getAbsolutePath();
        }
        break;
      case CSS:
        if (ext.equals("css")) {
          target = new File(base, s).getAbsolutePath();
        }
        break;
      default:
        throw new SmallerException("Invalid resource type " + type);
      }
    }
    return target;
  }

  private static class Context {

    private File inputZip;

    private File sourceDir;

    private File targetDir;

    private Manifest manifest;

  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy