Search in sources :

Example 1 with ArtifactRangeNotFoundException

use of co.cask.cdap.common.ArtifactRangeNotFoundException in project cdap by caskdata.

the class DefaultArtifactRepository method createParentClassLoader.

/**
 * Create a parent classloader using an artifact from one of the artifacts in the specified parents.
 *
 * @param artifactId the id of the artifact to create the parent classloader for
 * @param parentArtifacts the ranges of parents to create the classloader from
 * @return a classloader based off a parent artifact
 * @throws ArtifactRangeNotFoundException if none of the parents could be found
 * @throws InvalidArtifactException if one of the parents also has parents
 * @throws IOException if there was some error reading from the store
 */
private CloseableClassLoader createParentClassLoader(Id.Artifact artifactId, Set<ArtifactRange> parentArtifacts, EntityImpersonator entityImpersonator) throws ArtifactRangeNotFoundException, IOException, InvalidArtifactException {
    List<ArtifactDetail> parents = new ArrayList<>();
    for (ArtifactRange parentRange : parentArtifacts) {
        parents.addAll(artifactStore.getArtifacts(parentRange, Integer.MAX_VALUE, ArtifactSortOrder.UNORDERED));
    }
    if (parents.isEmpty()) {
        throw new ArtifactRangeNotFoundException(String.format("Artifact %s extends artifacts '%s' that do not exist", artifactId, Joiner.on('/').join(parentArtifacts)));
    }
    Location parentLocation = null;
    Location grandparentLocation = null;
    // complicated dependency trees that are hard to manage.
    for (ArtifactDetail parent : parents) {
        Set<ArtifactRange> grandparentRanges = parent.getMeta().getUsableBy();
        for (ArtifactRange grandparentRange : grandparentRanges) {
            // if the parent as the child as a parent (cyclic dependency)
            if (grandparentRange.getNamespace().equals(artifactId.getNamespace().getId()) && grandparentRange.getName().equals(artifactId.getName()) && grandparentRange.versionIsInRange(artifactId.getVersion())) {
                throw new InvalidArtifactException(String.format("Invalid artifact '%s': cyclic dependency. Parent '%s' has artifact '%s' as a parent.", artifactId, parent.getDescriptor().getArtifactId(), artifactId));
            }
            List<ArtifactDetail> grandparents = artifactStore.getArtifacts(grandparentRange, Integer.MAX_VALUE, ArtifactSortOrder.UNORDERED);
            // check that no grandparent has parents
            for (ArtifactDetail grandparent : grandparents) {
                Set<ArtifactRange> greatGrandparents = grandparent.getMeta().getUsableBy();
                if (!greatGrandparents.isEmpty()) {
                    throw new InvalidArtifactException(String.format("Invalid artifact '%s'. Grandparents of artifacts cannot have parents. Grandparent '%s' has parents.", artifactId, grandparent.getDescriptor().getArtifactId()));
                }
                // assumes any grandparent will do
                if (parentLocation == null && grandparentLocation == null) {
                    grandparentLocation = grandparent.getDescriptor().getLocation();
                }
            }
        }
        // assumes any parent will do
        if (parentLocation == null) {
            parentLocation = parent.getDescriptor().getLocation();
        }
    }
    List<Location> parentLocations = new ArrayList<>();
    parentLocations.add(parentLocation);
    if (grandparentLocation != null) {
        parentLocations.add(grandparentLocation);
    }
    return artifactClassLoaderFactory.createClassLoader(parentLocations.iterator(), entityImpersonator);
}
Also used : ArtifactRangeNotFoundException(co.cask.cdap.common.ArtifactRangeNotFoundException) ArrayList(java.util.ArrayList) ArtifactRange(co.cask.cdap.api.artifact.ArtifactRange) InvalidArtifactException(co.cask.cdap.common.InvalidArtifactException) Location(org.apache.twill.filesystem.Location)

Example 2 with ArtifactRangeNotFoundException

use of co.cask.cdap.common.ArtifactRangeNotFoundException in project cdap by caskdata.

the class ArtifactRepository method createParentClassLoader.

/**
   * Create a parent classloader using an artifact from one of the artifacts in the specified parents.
   *
   * @param artifactId the id of the artifact to create the parent classloader for
   * @param parentArtifacts the ranges of parents to create the classloader from
   * @return a classloader based off a parent artifact
   * @throws ArtifactRangeNotFoundException if none of the parents could be found
   * @throws InvalidArtifactException if one of the parents also has parents
   * @throws IOException if there was some error reading from the store
   */
private CloseableClassLoader createParentClassLoader(Id.Artifact artifactId, Set<ArtifactRange> parentArtifacts, EntityImpersonator entityImpersonator) throws ArtifactRangeNotFoundException, IOException, InvalidArtifactException {
    List<ArtifactDetail> parents = new ArrayList<>();
    for (ArtifactRange parentRange : parentArtifacts) {
        parents.addAll(artifactStore.getArtifacts(parentRange, Integer.MAX_VALUE, ArtifactSortOrder.UNORDERED));
    }
    if (parents.isEmpty()) {
        throw new ArtifactRangeNotFoundException(String.format("Artifact %s extends artifacts '%s' that do not exist", artifactId, Joiner.on('/').join(parentArtifacts)));
    }
    // check if any of the parents also have parents, which is not allowed. This is to simplify things
    // so that we don't have to chain a bunch of classloaders, and also to keep it simple for users to avoid
    // complicated dependency trees that are hard to manage.
    boolean isInvalid = false;
    StringBuilder errMsg = new StringBuilder("Invalid artifact '").append(artifactId).append("'.").append(" Artifact parents cannot have parents.");
    for (ArtifactDetail parent : parents) {
        Set<ArtifactRange> grandparents = parent.getMeta().getUsableBy();
        if (!grandparents.isEmpty()) {
            isInvalid = true;
            errMsg.append(" Parent '").append(parent.getDescriptor().getArtifactId().getName()).append("-").append(parent.getDescriptor().getArtifactId().getVersion().getVersion()).append("' has parents.");
        }
    }
    if (isInvalid) {
        throw new InvalidArtifactException(errMsg.toString());
    }
    // assumes any of the parents will do
    Location parentLocation = parents.get(0).getDescriptor().getLocation();
    return createArtifactClassLoader(parentLocation, entityImpersonator);
}
Also used : ArtifactRangeNotFoundException(co.cask.cdap.common.ArtifactRangeNotFoundException) ArrayList(java.util.ArrayList) ArtifactRange(co.cask.cdap.api.artifact.ArtifactRange) InvalidArtifactException(co.cask.cdap.common.InvalidArtifactException) Location(org.apache.twill.filesystem.Location)

Example 3 with ArtifactRangeNotFoundException

use of co.cask.cdap.common.ArtifactRangeNotFoundException in project cdap by caskdata.

the class ArtifactClient method add.

/**
 * Add an artifact.
 *
 * @param namespace the namespace to add the artifact to
 * @param artifactName the name of the artifact to add
 * @param artifactContents an input supplier for the contents of the artifact
 * @param artifactVersion the version of the artifact to add. If null, the version will be derived from the
 *                        manifest of the artifact
 * @param parentArtifacts the set of artifacts this artifact extends
 * @param additionalPlugins the set of plugins contained in the artifact that cannot be determined
 *                          through jar inspection. This set should include any classes that are plugins but could
 *                          not be annotated as such. For example, 3rd party classes like jdbc drivers fall into
 *                          this category.
 * @throws ArtifactAlreadyExistsException if the artifact already exists
 * @throws BadRequestException if the request is invalid. For example, if the artifact name or version is invalid
 * @throws ArtifactRangeNotFoundException if the parent artifacts do not exist
 * @throws IOException if a network error occurred
 * @throws UnauthenticatedException if the request is not authorized successfully in the gateway server
 */
public void add(NamespaceId namespace, String artifactName, InputSupplier<? extends InputStream> artifactContents, @Nullable String artifactVersion, @Nullable Set<ArtifactRange> parentArtifacts, @Nullable Set<PluginClass> additionalPlugins) throws ArtifactAlreadyExistsException, BadRequestException, IOException, UnauthenticatedException, ArtifactRangeNotFoundException, UnauthorizedException {
    URL url = config.resolveNamespacedURLV3(namespace, String.format("artifacts/%s", artifactName));
    HttpRequest.Builder requestBuilder = HttpRequest.post(url);
    if (artifactVersion != null) {
        requestBuilder.addHeader("Artifact-Version", artifactVersion);
    }
    if (parentArtifacts != null && !parentArtifacts.isEmpty()) {
        requestBuilder.addHeader("Artifact-Extends", Joiner.on('/').join(parentArtifacts));
    }
    if (additionalPlugins != null && !additionalPlugins.isEmpty()) {
        requestBuilder.addHeader("Artifact-Plugins", GSON.toJson(additionalPlugins));
    }
    HttpRequest request = requestBuilder.withBody(artifactContents).build();
    HttpResponse response = restClient.execute(request, config.getAccessToken(), HttpURLConnection.HTTP_CONFLICT, HttpURLConnection.HTTP_BAD_REQUEST, HttpURLConnection.HTTP_NOT_FOUND);
    int responseCode = response.getResponseCode();
    if (responseCode == HttpURLConnection.HTTP_CONFLICT) {
        throw new ArtifactAlreadyExistsException(response.getResponseBodyAsString());
    } else if (responseCode == HttpURLConnection.HTTP_BAD_REQUEST) {
        throw new BadRequestException(response.getResponseBodyAsString());
    } else if (responseCode == HttpURLConnection.HTTP_NOT_FOUND) {
        throw new ArtifactRangeNotFoundException(parentArtifacts);
    }
}
Also used : HttpRequest(co.cask.common.http.HttpRequest) ArtifactRangeNotFoundException(co.cask.cdap.common.ArtifactRangeNotFoundException) ArtifactAlreadyExistsException(co.cask.cdap.common.ArtifactAlreadyExistsException) HttpResponse(co.cask.common.http.HttpResponse) BadRequestException(co.cask.cdap.common.BadRequestException) URL(java.net.URL)

Example 4 with ArtifactRangeNotFoundException

use of co.cask.cdap.common.ArtifactRangeNotFoundException in project cdap by caskdata.

the class ArtifactHttpHandler method addArtifact.

@POST
@Path("/namespaces/{namespace-id}/artifacts/{artifact-name}")
@AuditPolicy(AuditDetail.HEADERS)
public BodyConsumer addArtifact(HttpRequest request, HttpResponder responder, @PathParam("namespace-id") final String namespaceId, @PathParam("artifact-name") final String artifactName, @HeaderParam(VERSION_HEADER) final String artifactVersion, @HeaderParam(EXTENDS_HEADER) final String parentArtifactsStr, @HeaderParam(PLUGINS_HEADER) String pluginClasses) throws NamespaceNotFoundException, BadRequestException {
    final NamespaceId namespace = validateAndGetNamespace(namespaceId);
    // and validated there
    if (artifactVersion != null && !artifactVersion.isEmpty()) {
        validateAndGetArtifactId(namespace, artifactName, artifactVersion);
    }
    final Set<ArtifactRange> parentArtifacts = parseExtendsHeader(namespace, parentArtifactsStr);
    final Set<PluginClass> additionalPluginClasses;
    if (pluginClasses == null) {
        additionalPluginClasses = ImmutableSet.of();
    } else {
        try {
            additionalPluginClasses = GSON.fromJson(pluginClasses, PLUGINS_TYPE);
        } catch (JsonParseException e) {
            responder.sendString(HttpResponseStatus.BAD_REQUEST, String.format("%s header '%s' is invalid: %s", PLUGINS_HEADER, pluginClasses, e.getMessage()));
            return null;
        }
    }
    try {
        // copy the artifact contents to local tmp directory
        final File destination = File.createTempFile("artifact-", ".jar", tmpDir);
        return new AbstractBodyConsumer(destination) {

            @Override
            protected void onFinish(HttpResponder responder, File uploadedFile) {
                try {
                    String version = (artifactVersion == null || artifactVersion.isEmpty()) ? getBundleVersion(uploadedFile) : artifactVersion;
                    ArtifactId artifactId = validateAndGetArtifactId(namespace, artifactName, version);
                    // add the artifact to the repo
                    artifactRepository.addArtifact(Id.Artifact.fromEntityId(artifactId), uploadedFile, parentArtifacts, additionalPluginClasses);
                    responder.sendString(HttpResponseStatus.OK, "Artifact added successfully");
                } catch (ArtifactRangeNotFoundException e) {
                    responder.sendString(HttpResponseStatus.NOT_FOUND, e.getMessage());
                } catch (ArtifactAlreadyExistsException e) {
                    responder.sendString(HttpResponseStatus.CONFLICT, e.getMessage());
                } catch (WriteConflictException e) {
                    responder.sendString(HttpResponseStatus.INTERNAL_SERVER_ERROR, "Conflict while writing artifact, please try again.");
                } catch (IOException e) {
                    LOG.error("Exception while trying to write artifact {}-{}-{}.", namespaceId, artifactName, artifactVersion, e);
                    responder.sendString(HttpResponseStatus.INTERNAL_SERVER_ERROR, "Error performing IO while writing artifact.");
                } catch (BadRequestException e) {
                    responder.sendString(HttpResponseStatus.BAD_REQUEST, e.getMessage());
                } catch (UnauthorizedException e) {
                    responder.sendString(HttpResponseStatus.FORBIDDEN, e.getMessage());
                } catch (Exception e) {
                    LOG.error("Error while writing artifact {}-{}-{}", namespaceId, artifactName, artifactVersion, e);
                    responder.sendString(HttpResponseStatus.INTERNAL_SERVER_ERROR, "Error while adding artifact.");
                }
            }

            private String getBundleVersion(File file) throws BadRequestException, IOException {
                try (JarFile jarFile = new JarFile(file)) {
                    Manifest manifest = jarFile.getManifest();
                    if (manifest == null) {
                        throw new BadRequestException("Unable to derive version from artifact because it does not contain a manifest. " + "Please package the jar with a manifest, or explicitly specify the artifact version.");
                    }
                    Attributes attributes = manifest.getMainAttributes();
                    String version = attributes == null ? null : attributes.getValue(ManifestFields.BUNDLE_VERSION);
                    if (version == null) {
                        throw new BadRequestException("Unable to derive version from artifact because manifest does not contain Bundle-Version attribute. " + "Please include Bundle-Version in the manifest, or explicitly specify the artifact version.");
                    }
                    return version;
                } catch (ZipException e) {
                    throw new BadRequestException("Artifact is not in zip format. Please make sure it is a jar file.");
                }
            }
        };
    } catch (IOException e) {
        LOG.error("Exception creating temp file to place artifact {} contents", artifactName, e);
        responder.sendString(HttpResponseStatus.INTERNAL_SERVER_ERROR, "Server error creating temp file for artifact.");
        return null;
    }
}
Also used : ArtifactRangeNotFoundException(co.cask.cdap.common.ArtifactRangeNotFoundException) HttpResponder(co.cask.http.HttpResponder) ArtifactId(co.cask.cdap.proto.id.ArtifactId) ArtifactRange(co.cask.cdap.api.artifact.ArtifactRange) Attributes(java.util.jar.Attributes) ZipException(java.util.zip.ZipException) IOException(java.io.IOException) JsonParseException(com.google.gson.JsonParseException) JarFile(java.util.jar.JarFile) Manifest(java.util.jar.Manifest) InvalidArtifactRangeException(co.cask.cdap.api.artifact.InvalidArtifactRangeException) NamespaceNotFoundException(co.cask.cdap.common.NamespaceNotFoundException) ArtifactNotFoundException(co.cask.cdap.common.ArtifactNotFoundException) ZipException(java.util.zip.ZipException) ArtifactAlreadyExistsException(co.cask.cdap.common.ArtifactAlreadyExistsException) InvocationTargetException(java.lang.reflect.InvocationTargetException) BadRequestException(co.cask.cdap.common.BadRequestException) UnauthorizedException(co.cask.cdap.security.spi.authorization.UnauthorizedException) JsonParseException(com.google.gson.JsonParseException) PluginNotExistsException(co.cask.cdap.internal.app.runtime.plugin.PluginNotExistsException) ArtifactRangeNotFoundException(co.cask.cdap.common.ArtifactRangeNotFoundException) WriteConflictException(co.cask.cdap.internal.app.runtime.artifact.WriteConflictException) JsonSyntaxException(com.google.gson.JsonSyntaxException) IOException(java.io.IOException) NotFoundException(co.cask.cdap.common.NotFoundException) ArtifactAlreadyExistsException(co.cask.cdap.common.ArtifactAlreadyExistsException) AbstractBodyConsumer(co.cask.cdap.common.http.AbstractBodyConsumer) WriteConflictException(co.cask.cdap.internal.app.runtime.artifact.WriteConflictException) UnauthorizedException(co.cask.cdap.security.spi.authorization.UnauthorizedException) BadRequestException(co.cask.cdap.common.BadRequestException) NamespaceId(co.cask.cdap.proto.id.NamespaceId) PluginClass(co.cask.cdap.api.plugin.PluginClass) JarFile(java.util.jar.JarFile) File(java.io.File) Path(javax.ws.rs.Path) AuditPolicy(co.cask.cdap.common.security.AuditPolicy) POST(javax.ws.rs.POST)

Aggregations

ArtifactRangeNotFoundException (co.cask.cdap.common.ArtifactRangeNotFoundException)4 ArtifactRange (co.cask.cdap.api.artifact.ArtifactRange)3 ArtifactAlreadyExistsException (co.cask.cdap.common.ArtifactAlreadyExistsException)2 BadRequestException (co.cask.cdap.common.BadRequestException)2 InvalidArtifactException (co.cask.cdap.common.InvalidArtifactException)2 ArrayList (java.util.ArrayList)2 Location (org.apache.twill.filesystem.Location)2 InvalidArtifactRangeException (co.cask.cdap.api.artifact.InvalidArtifactRangeException)1 PluginClass (co.cask.cdap.api.plugin.PluginClass)1 ArtifactNotFoundException (co.cask.cdap.common.ArtifactNotFoundException)1 NamespaceNotFoundException (co.cask.cdap.common.NamespaceNotFoundException)1 NotFoundException (co.cask.cdap.common.NotFoundException)1 AbstractBodyConsumer (co.cask.cdap.common.http.AbstractBodyConsumer)1 AuditPolicy (co.cask.cdap.common.security.AuditPolicy)1 WriteConflictException (co.cask.cdap.internal.app.runtime.artifact.WriteConflictException)1 PluginNotExistsException (co.cask.cdap.internal.app.runtime.plugin.PluginNotExistsException)1 ArtifactId (co.cask.cdap.proto.id.ArtifactId)1 NamespaceId (co.cask.cdap.proto.id.NamespaceId)1 UnauthorizedException (co.cask.cdap.security.spi.authorization.UnauthorizedException)1 HttpRequest (co.cask.common.http.HttpRequest)1