use of io.cdap.cdap.common.ArtifactRangeNotFoundException in project cdap by caskdata.
the class DefaultArtifactRepository method getParentArtifactDescriptors.
/**
* Get {@link ArtifactDescriptor} of parent and grandparent (if any) artifacts for the given artifact.
*
* @param artifactId the id of the artifact for which to find its parent and grandparent {@link ArtifactDescriptor}
* @param parentArtifacts the ranges of parents to find
* @return {@link ArtifactDescriptor} of parent and grandparent (if any) artifacts, in that specific order
* @throws ArtifactRangeNotFoundException if none of the parents could be found
* @throws InvalidArtifactException if one of the parents also has parents
*/
private List<ArtifactDescriptor> getParentArtifactDescriptors(Id.Artifact artifactId, Set<ArtifactRange> parentArtifacts) throws ArtifactRangeNotFoundException, 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)));
}
ArtifactDescriptor parentArtifact = null;
ArtifactDescriptor grandparentArtifact = 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 (parentArtifact == null && grandparentArtifact == null) {
grandparentArtifact = grandparent.getDescriptor();
}
}
}
// assumes any parent will do
if (parentArtifact == null) {
parentArtifact = parent.getDescriptor();
}
}
List<ArtifactDescriptor> parentArtifactList = new ArrayList<>();
parentArtifactList.add(parentArtifact);
if (grandparentArtifact != null) {
parentArtifactList.add(grandparentArtifact);
}
return parentArtifactList;
}
use of io.cdap.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);
// that processes the last http chunk.
if (artifactVersion != null && !artifactVersion.isEmpty()) {
ArtifactId artifactId = validateAndGetArtifactId(namespace, artifactName, artifactVersion);
// If the artifact ID is available, use it to perform an authorization check.
contextAccessEnforcer.enforce(artifactId, StandardPermission.CREATE);
} else {
// If there is no version, we perform an enforceOnParent check in which the entityID is not needed.
contextAccessEnforcer.enforceOnParent(EntityType.ARTIFACT, namespace, StandardPermission.CREATE);
}
final Set<ArtifactRange> parentArtifacts = parseExtendsHeader(namespace, parentArtifactsStr);
final Set<PluginClass> additionalPluginClasses;
if (pluginClasses == null || pluginClasses.isEmpty()) {
additionalPluginClasses = ImmutableSet.of();
} else {
try {
additionalPluginClasses = GSON.fromJson(pluginClasses, PLUGINS_TYPE);
additionalPluginClasses.forEach(PluginClass::validate);
} catch (JsonParseException e) {
throw new BadRequestException(String.format("%s header '%s' is invalid.", PLUGINS_HEADER, pluginClasses), e);
} catch (IllegalArgumentException e) {
throw new BadRequestException(String.format("Invalid PluginClasses '%s'.", pluginClasses), e);
}
}
try {
// copy the artifact contents to local tmp directory
Files.createDirectories(tmpDir.toPath());
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;
}
}
use of io.cdap.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 a provider 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, ContentProvider<? 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);
}
}
Aggregations