Package maqetta.core.server.command

Source Code of maqetta.core.server.command.Download

package maqetta.core.server.command;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

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

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import org.davinci.ajaxLibrary.Library;
import org.davinci.server.user.IUser;
import org.davinci.server.util.JSONReader;
import org.davinci.server.util.JSONWriter;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.maqetta.server.Command;
import org.maqetta.server.IDavinciServerConstants;
import org.maqetta.server.IVResource;
import org.maqetta.server.ServerManager;
import org.maqetta.server.VLibraryResource;

public class Download extends Command {

  static final private Logger theLogger = Logger.getLogger(Download.class.getName());
 
  private Vector<String> zippedEntries;
  private URL buildURL = null;
 
  //This should stay in sync with validation rules on the client
  public static final String DOWNLOAD_FILE_REPLACE_REGEXP = "[^a-zA-z0-9_.]";
  public static String buildBase = "http://build.dojotoolkit.org";

  {
    String builderProp = ServerManager.getServerManager().getDavinciProperty(IDavinciServerConstants.DOJO_WEB_BUILDER);
    if (builderProp != null) {
      buildBase = builderProp;
    }
  }

  @SuppressWarnings("unchecked")
  public void handleCommand(HttpServletRequest req, HttpServletResponse resp, IUser user) throws IOException {
      // SECURITY, VALIDATION
      //   'fileName': XXX not validated
      //   'resources': contents eventually checked by User.getResource()
      //   'libs': XXX validated?
      //   'root': XXX not validated

      if (user == null) {
            return;
        }
       
        /* keep track of things we've added */
        zippedEntries = new Vector<String>();
       
        String path = req.getParameter("fileName");
        path = sanitizeFileName(path);
        String res = req.getParameter("resources");
        String libs = req.getParameter("libs");
        String root = req.getParameter("root");
        String build = req.getParameter("build");
        boolean useFullSource = "1".equals(req.getParameter("fullsource"));
      
       
        if (build != null){
          //TODO: should put this in a separate Thread
          this.buildURL = getBuildURL(user, req.getRequestURL().toString());
        }

    ArrayList<String> o = (ArrayList<String>) JSONReader.read(res);
        List<Map<String, String>> lib = null;
        boolean includeLibs = true;
        if(libs!=null){
          lib = (List<Map<String, String>>) JSONReader.read(libs);
          includeLibs= false;
        }
        String[] resources = o.toArray(new String[o.size()]);

        IVResource[] files = new IVResource[resources.length];
        for (int i = 0; i < files.length; i++) {
            files[i] = user.getResource(resources[i]);
        }

        try {
            resp.setContentType("application/x-download");
            resp.setHeader("Content-Disposition", "attachment; filename=" + path);

            ZipOutputStream zos = new ZipOutputStream(resp.getOutputStream());
            IPath rootPath = new Path(root);
            zipFiles(files, rootPath, zos, includeLibs);
            if(lib!=null) zipLibs(lib, rootPath, zos, useFullSource);
            zos.close();
            // responseString="OK";

        } catch (IOException e) {
            responseString = "Error creating download file : " + e;
        }
    }

  private URL getBuildURL(IUser user, String requestURLString) throws IOException {
    URL buildURL = new URL(buildBase);
    try {
          Map<String, List<String>> dependencies = analyzeWorkspace(user, requestURLString);
          if (dependencies == null) {
            // nothing to build. Proceed to download zip without any build step.
            return null;
          }
        String statusCookie = requestBuild(dependencies);
        URL status = new URL(buildURL, statusCookie);
        String result = null;
        while (result == null) {
                // now poll for a response with a "result" property
          InputStream is = status.openStream();
          try {
                    int size;
                    byte[] buffer = new byte[2048];
    
                    ByteArrayOutputStream baos =
                            new ByteArrayOutputStream(buffer.length);
    
                    while ((size = is.read(buffer, 0, buffer.length)) != -1) {
                        baos.write(buffer, 0, size);
                    }               
              String content = baos.toString();
              theLogger.finest("build status: " + content);
              @SuppressWarnings("unchecked")
          Map<String, String> json = (Map<String, String>)JSONReader.read(content);
              result = json.get("result");
          } finally {
            is.close();
          }
          Thread.sleep(1000);
        }

        theLogger.fine("build result: " + result);
        buildURL = new URL(buildURL, result);
        } catch (InterruptedException ie) {
          throw new IOException("Thread interrupted.  Did not obtain build result.");
        }
        return buildURL;
  }

  private Map<String, List<String>> analyzeWorkspace(IUser user, String requestURL) throws IOException {
      IVResource[] files = user.findFiles("*.html", false, true);
        HttpClient client = new HttpClient();
        PostMethod method;
        Map<String, List<String>> result = null;
        String userID = user.getUserID();

        theLogger.finest("analyzeWorkspace: number of files: " + files.length);
    for (int i = 0; i < files.length; i++) {
          if (files[i].isVirtual()) {
            theLogger.finest("isVirtual name="+files[i].getPath());
            continue;
          }
      method = new PostMethod(buildBase + "/api/dependencies");
        try {
              String url = new URL(new URL(requestURL), "/maqetta/user/" + userID + "/ws/workspace/" + files[i].getPath()).toExternalForm();
              theLogger.finest("DWB: Analyse url="+url);
              Part[] parts = {
                new StringPart("value", url, "utf-8"),
                new StringPart("type", "URL")
              };
              method.setRequestEntity(new MultipartRequestEntity(parts, method.getParams()));
              int statusCode = client.executeMethod(method);
              String body = method.getResponseBodyAsString();
              if (statusCode != HttpStatus.SC_OK) {
                  throw new IOException(buildBase + "/api/dependencies: Analyse failed: " + method.getStatusLine() + "\n" + body);
              }

              int start = body.indexOf("<textarea>");
              int end = body.indexOf("</textarea>");
              if (start == -1 || end == -1) {
                throw new IOException(buildBase + "/api/dependencies: unable to parse output: " + body);
              }

              String content = body.substring(start + 10, end);
              theLogger.finest("DWB: Analyse result="+ content);
              @SuppressWarnings("unchecked")
        Map<String, List<String>> dependencies = (Map<String, List<String>>)JSONReader.read(content);
              if (result == null) {
                result = dependencies;            
              } else {
                List<String> requiredDojoModules = result.get("requiredDojoModules");
                List<String> additionalModules = dependencies.get("requiredDojoModules");
                // TODO: Does Java provide a better way to merge lists?  Sets?  Use an Iterator here?
                  for(int j = 0; j < additionalModules.size(); j++) {
                    String additionalModule = additionalModules.get(j);
                    if (!requiredDojoModules.contains(additionalModule)) {
                      requiredDojoModules.add(additionalModule);
                      theLogger.finest("add "+additionalModule);
                    }
                  }
              }
        } finally {
          method.releaseConnection();
        }
      }
        return result;
    }

    private String requestBuild(Map<String, List<String>> dependencies) throws IOException {
        JSONWriter jsonWriter = new JSONWriter(false);
        jsonWriter/*.startObject()*/
          .addField("optimise", "shrinksafe")
          .addField("cdn", "none")
          .addField("platforms", "all")
          .addField("themes", "claro")
          .addField("cssOptimise", "comments");
        jsonWriter.addFieldName("packages").startArray();
        jsonWriter.startObject().addField("name", "dojo").addField("version","1.8.0").endObject();
//TODO: add supplemental packages like maqetta.*
//        jsonWriter.startObject().addField("name", supplemental).addField("version","1.0.0").endObject();
        jsonWriter.endArray();
        jsonWriter.addFieldName("layers").startArray();
        jsonWriter.startObject();
        jsonWriter.addField("name", "dojo.js");
        jsonWriter.addFieldName("modules");
        jsonWriter.startArray();
      List<String> requiredDojoModules = dependencies.get("requiredDojoModules");
        for(int i = 0; i < requiredDojoModules.size(); i++) {
            jsonWriter.startObject();
          jsonWriter.addField("name", requiredDojoModules.get(i));
          jsonWriter.addField("package", "dojo");
            jsonWriter.endObject();
        }
        jsonWriter.endArray();
        jsonWriter.endObject();
        jsonWriter.endArray();
//        jsonWriter.endObject();
        String content = jsonWriter.getJSON();

        HttpClient client = new HttpClient();
        PostMethod method = new PostMethod(buildBase + "/api/build");
        theLogger.finest("/api/build: " + content);
        try {
          method.setRequestEntity(new StringRequestEntity(content, "application/json", "utf-8"));
            int statusCode = client.executeMethod(method);
            String json = method.getResponseBodyAsString();
            theLogger.finest("/api/build response: " + json);
          if (statusCode != HttpStatus.SC_ACCEPTED && statusCode != HttpStatus.SC_OK) {
            throw new IOException(buildBase + "/api/build failed with status: " + statusCode + "\n" + json);
          }
            @SuppressWarnings("unchecked")
      Map<String, String> status = (Map<String, String>)JSONReader.read(json);
            String statusLink = status.get("buildStatusLink");
            if (statusLink == null) {
              throw new IOException(buildBase + "/api/build failed with error: " + status.get("error"));
            }
            return statusLink;
        } finally {
      method.releaseConnection();
    }
    }

    private void transferBuildStream(URL resultURL, String dirString, ZipOutputStream zos) throws IOException {
    ZipInputStream zis = new ZipInputStream(new BufferedInputStream(resultURL.openStream()));
    ZipEntry entry;
        byte[] readBuffer = new byte[2156];
        int bytesIn;

        try {
          while ((entry = zis.getNextEntry()) != null) {
            String pathString = dirString.concat(entry.getName());
        // place the zip entry in the ZipOutputStream object
        zos.putNextEntry(new ZipEntry(pathString));
            theLogger.finest("transferBuildStream:" + pathString);
              // now write the content of the file to the ZipOutputStream
        while ((bytesIn = zis.read(readBuffer)) != -1) {
          zos.write(readBuffer, 0, bytesIn);
        }
      }
        } finally {
          zis.close();
        }
    }

    private String sanitizeFileName(String fileName) {
      if (fileName == null || fileName.equals("")) {
        fileName = "workspace1.zip";
      }
      fileName = fileName.replaceAll(DOWNLOAD_FILE_REPLACE_REGEXP, "_");
      return fileName;
    }
   
    private boolean addEntry(String path){
     
      for(int i=0;i<this.zippedEntries.size();i++){
        String entry = zippedEntries.get(i);
        if(entry.compareTo(path)==0) return false;
      }
      zippedEntries.add(path);
      return true;
    }
   
    private void zipLibs(List<Map<String, String>> libs, IPath root, ZipOutputStream zos, boolean useSource) throws IOException{
        for (int i = 0; i < libs.size(); i++) {
            Map<String, String> libEntry = (Map<String, String>) libs.get(i);
            String id = libEntry.get("id");
            String version = libEntry.get("version");
            String path = libEntry.get("root");
            Library lib = ServerManager.getServerManager().getLibraryManager().getLibrary(id, version);
           
            boolean sourceLibrary = lib.getSourcePath() != null && useSource;
           
            IVResource libResource = new VLibraryResource(lib, lib.getURL("", sourceLibrary),path, "",sourceLibrary);
            zipDir(libResource,root, zos, true);
        }
    }
   

    private void zipDir(IVResource zipDir, IPath root, ZipOutputStream zos, boolean includeLibs) throws IOException {
        IVResource[] dirList = zipDir.listFiles();
       
        if(!includeLibs && zipDir instanceof VLibraryResource)
          return;
       
        zipFiles(dirList, root, zos,includeLibs);
    }

    private void zipFiles(IVResource[] files, IPath root, ZipOutputStream zos, boolean includeLibs) throws IOException {
        byte[] readBuffer = new byte[2156];
        int bytesIn;
        // loop through dirList, and zip the files
        for (int i = 0; i < files.length; i++) {
            if (files[i].isDirectory()) {
                zipDir(files[i], root, zos,includeLibs);
                continue;
            }
            if(!includeLibs && files[i] instanceof VLibraryResource)
              continue;

          IPath filePath = new Path(files[i].getPath());
            String pathString = filePath.removeFirstSegments(filePath.matchingFirstSegments(root)).toString();
           
            if(pathString==null) return;
           
            /* remove leading characters that confuse and anger windows built in archive util */
            if(pathString.length() > 1 && pathString.indexOf("./")==0)
             pathString = pathString.substring(2);

            if(!addEntry(pathString)) continue;

            String dirString = pathString.substring(0, pathString.lastIndexOf('/') + 1);
            if (this.buildURL != null && files[i].getPath().equals("lib/dojo/dojo/dojo.js")) {
            // substitute built version of dojo.js provided by the webservice, along with associated generated layers like nls
            transferBuildStream(this.buildURL, dirString, zos);
          } else {
            // copy stream to zip directly
                InputStream is = null;
                try {
                is = files[i].getInputStreem();           

                // place the zip entry in the ZipOutputStream object
                zos.putNextEntry(new ZipEntry(pathString));
                // now write the content of the file to the ZipOutputStream
                while ((bytesIn = is.read(readBuffer)) != -1) {
                  zos.write(readBuffer, 0, bytesIn);
                }
                } finally {
                    // close the Stream
                  if (is != null) is.close();
                  files[i].removeWorkingCopy();
                }
          }
       }
    }
}
TOP

Related Classes of maqetta.core.server.command.Download

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.