Package org.dtk.resources

Source Code of org.dtk.resources.Packages

package org.dtk.resources;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;

import org.apache.commons.io.FileUtils;
import org.apache.wink.common.model.multipart.BufferedInMultiPart;
import org.dtk.resources.dependencies.ScriptParser;
import org.dtk.resources.exceptions.ConfigurationException;
import org.dtk.resources.exceptions.IncorrectParameterException;
import org.dtk.resources.packages.PackageRepository;
import org.dtk.util.HttpUtil;
import org.dtk.util.JsonUtil;

/**
* RESTful Packages API. This API provides access to the packages
* available to the build system, allowing a user to retrieve a list of all
* packages present, retrieve details about specific packages (versions and
* modules) and create temporary packages from user applications.
*
* @author James Thomas
*/

@Path("/packages")
public class Packages {
  /** Global access to servlet and uri information */
  @Context ServletConfig config;
  @Context UriInfo info;

  /** Form submission field containing user application */
  protected static final String USER_APP_FIELD = "user_app";
 
  /** Arbitrary package version for temporary packages */
  protected static final String temporaryPackageVersion = "1.0.0";

  /** Resource path format, "context_path/servet_path/resource_path" */
  protected static final String resourcePathFormat = "%1$s%2$s";

  /** Response messages **/
  /** Generic failure creating temporary package from uploaded application. */
  protected static final String fatalProcessingErrorMsg
  = "Internal server error, unable to process new package";
 
  /** Unable to decompress user application error message */
  protected static final String invalidRequestErrorMsg
  = "Invalid client request, unable to decompress user application.";
 
  /** Request missing mandatory application parameter */
  protected static final String missingAppParameterErrorMsg
  = "Invalid client request, missing mandatory field containing user application";
 
  /** Log messages **/
  /** File path for decompressed temporary application is not valid */
  protected static final String errorCreatingPackageLogMsg
  = "Fatal error decompressing user's application into a temporary package, details: %1$s";

  /** Unable to construct valid URL for new temporary application */
  protected static final String errorCreatingPackageLocationLogMsg
  = "Unable to construct valid URL for new temporary package, details: %1$s";
 
  /** Packages logging class */
  protected static Logger logger = Logger.getLogger(Packages.class.getName());
 
  /**
   * Retrieve the global packages list and build options
   * available.
   *
   * @return Packages information
   * @throws ConfigurationException - Couldn't access package details due to configuration error.
   */
  @GET
  @Produces(MediaType.APPLICATION_JSON)
  public HashMap<String, Object> getPackages(@Context HttpServletRequest request) throws ConfigurationException {
    logger.entering(this.getClass().getName(), "getPackages");
    HashMap<String, Object> packageBuildOptions = null;

    // Instantiate package repository and retrieve default build options
    PackageRepository packageRepo = PackageRepository.getInstance();
    packageBuildOptions = packageRepo.getBuildParameters();

    // Add all available packages and versions information to the build options
    List<String> packageNames = packageRepo.getPackages();
    packageBuildOptions.put("packages", getResourceDetails(request, packageNames));

    logger.exiting(this.getClass().getName(), "getPackages");
    return packageBuildOptions;
 

  /**
   * Retrieve versions available for a given package.
   *
   * @param name - Package identifier
   * @return List of available versions
   * @throws ConfigurationException - Couldn't access package details due to configuration error.
   */
  @GET
  @Path("{name}")
  @Produces(MediaType.APPLICATION_JSON)
  public List<Map<String, String>> getPackage(@Context HttpServletRequest request, @PathParam("name") String name) { 
    logger.entering(this.getClass().getName(), "getPackage");
   
    PackageRepository packageRepo = PackageRepository.getInstance();
    List<String> packageVersions = packageRepo.getPackageVersions(name);   
    List<Map<String, String>> packageDetails = getResourceDetails(request, packageVersions);
   
    logger.exiting(this.getClass().getName(), "getPackage");
    return packageDetails;
  }

  /**
   * Retrieve package meta-data for requested parameters. 
   *
   * @param id - Package identifier
   * @param version - Package version
   * @return Package modules, name and description.
   */
  @GET
  @Path("{name}/{version}")
  @Produces(MediaType.APPLICATION_JSON)
  public HashMap<String, Object> getPackageDetails(@PathParam("name") String id, @PathParam("version") String version) {
    logger.entering(this.getClass().getName(), "getPackageDetails");
   
    HashMap<String, Object> packageDetails = null;

    // Retrieve package meta-data, HTTP 404 if invalid name/version given.
    PackageRepository packageRepo = PackageRepository.getInstance();
    packageDetails = packageRepo.getPackageDetails(id, version);

    // Remove package location from response, not relevant for the user.
    packageDetails.remove("location");
   
    logger.exiting(this.getClass().getName(), "getPackageDetails");   
    return packageDetails;
  }
 
  /**
   * Create a new temporary package resource. Client must include a compressed user application
   * in the request, present under the "user_app" key. This application will be uncompressed and
   * stored in a temporary location. The temporary package identifier and module details will be
   * returned to the user. HTTP status code 201 indicates a successful request. 
   *
   * @param multiPartForm - Form submission parameters, must contain user_app.
   * @return String - HTML encoded JSON package representation.
   * @throws URISyntaxException
   */
  @POST
  @Consumes(MediaType.MULTIPART_FORM_DATA)
  @Produces(MediaType.TEXT_HTML)   
  public Response createTemporaryPackage(BufferedInMultiPart multiPartForm) {   
    logger.entering(this.getClass().getName(), "createTemporaryPackage");
    // Complex response needed, HTTP 201.
    Response created = null;
   
    // Process multipart form data into useful format. All form part bodies will be extracted
    // when it has a name property and stored as strings.
    MultivaluedMap<String, Object> formFields = HttpUtil.retrieveMultiPartFormValues(multiPartForm, InputStream.class);

    if (formFields.containsKey(USER_APP_FIELD)) {
      // Map, holding package details, that will be rendered as JSON.
      Map<String, Object> temporaryPackageDetails = new HashMap<String, Object>();

      PackageRepository packageRepo = PackageRepository.getInstance();

      try {
        // Decompress temporary package, returning unique reference
        String packageIdentifier = packageRepo.createTemporaryPackage((InputStream) formFields.getFirst(USER_APP_FIELD));
       
        // Find temporary package location from identifier
        String packageLocation = packageRepo.getPackageLocation(packageIdentifier, temporaryPackageVersion);

        Iterator<File> packageJsFilesIter = FileUtils.iterateFiles(new File(packageLocation),
          new String[]{"js"}, true);

        // List holding module definitions & requirements
        List<String> modulesProvided = new ArrayList<String>();
        List<String> modulesRequired = new ArrayList<String>();
       
        // Hold new temporary package details
        Map<String, String> packageDetails = new HashMap<String, String>();

        // Run script parser on each file, pulling out requires and provides
        while(packageJsFilesIter.hasNext()) {
          String fileContents = FileUtils.readFileToString(packageJsFilesIter.next());
          ScriptParser scriptParser = new ScriptParser(fileContents);

          // Add all module dependencies discovered to global state holder
          modulesProvided.addAll(scriptParser.retrieveModuleProvides());
          modulesRequired.addAll(scriptParser.retrieveModuleRequires());
        }

        // Store package modules, requires & provides, in response JSON.
        temporaryPackageDetails.put("requiredDojoModules", modulesRequired);
        temporaryPackageDetails.put("availableModules", modulesProvided);
       
        // Temporary package details, use arbitrary version for temporary packages.
        packageDetails.put("name", packageIdentifier);
        packageDetails.put("version", "1.0.0");
       
        // Store package reference in the details object
        temporaryPackageDetails.put("packages", Arrays.asList(packageDetails));

        // Render provides, requires and temporary package id as JSON
        String htmlEncodedJsonPackageDetails = JsonUtil.writeJavaToHtmlEncodedJson(temporaryPackageDetails);
       
        // Construct HTTP 201 response, containing text body.
        created = Response.created(new URI(packageLocation)).entity(htmlEncodedJsonPackageDetails).build();
      } catch (FileNotFoundException e) {
        logger.log(Level.SEVERE, String.format(errorCreatingPackageLogMsg, e.getMessage()));
        throw new ConfigurationException(fatalProcessingErrorMsg);
      } catch (IOException e) {
        logger.log(Level.SEVERE, String.format(errorCreatingPackageLogMsg, e.getMessage()));
        throw new IncorrectParameterException(invalidRequestErrorMsg);
      } catch (URISyntaxException e) {
        logger.log(Level.SEVERE, String.format(errorCreatingPackageLocationLogMsg, e.getMessage()));
        throw new ConfigurationException(fatalProcessingErrorMsg);
      }
    } else {
      throw new IncorrectParameterException(missingAppParameterErrorMsg);
    }

    logger.exiting(this.getClass().getName(), "createTemporaryPackage")
    return created;
  }

  /**
   * Construct resource collections details, map of names and links to resources.
   * Using current request context to obtain request URL.
   *
   * @param request - Current HTTP request
   * @return Collection of resource details
   */
  protected List<Map<String, String>> getResourceDetails(HttpServletRequest request, List<String> resourceIds) {
    List<Map<String, String>> packageDetails = new ArrayList<Map<String, String>>();

    // For each package, store name and version link
    Iterator<String> iter = resourceIds.iterator();
    while (iter.hasNext()) {
      final String resourceId = iter.next();
      // Make sure we end with a slash
      String requestURL = request.getRequestURI();     
      if (!requestURL.endsWith("/")) {
        requestURL += "/";
      }
      final String resourcePath = String.format(resourcePathFormat, requestURL, resourceId);     
      packageDetails.add(new HashMap<String, String>() {{
        put("name", resourceId);
        put("link", resourcePath);
      }});

    }

    return packageDetails;
  }
}
TOP

Related Classes of org.dtk.resources.Packages

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.