use of io.cdap.cdap.api.artifact.ArtifactRange in project cdap by caskdata.
the class DefaultArtifactRepository method validateParentSet.
/**
* Validates the parents of an artifact. Checks that each artifact only appears with a single version range.
*
* @param parents the set of parent ranges to validate
* @throws InvalidArtifactException if there is more than one version range for an artifact
*/
@VisibleForTesting
static void validateParentSet(Id.Artifact artifactId, Set<ArtifactRange> parents) throws InvalidArtifactException {
boolean isInvalid = false;
StringBuilder errMsg = new StringBuilder("Invalid parents field.");
// check for multiple version ranges for the same artifact.
// ex: "parents": [ "etlbatch[1.0.0,2.0.0)", "etlbatch[3.0.0,4.0.0)" ]
Set<String> parentNames = new HashSet<>();
// keep track of dupes so that we don't have repeat error messages if there are more than 2 ranges for a name
Set<String> dupes = new HashSet<>();
for (ArtifactRange parent : parents) {
String parentName = parent.getName();
if (!parentNames.add(parentName) && !dupes.contains(parentName)) {
errMsg.append(" Only one version range for parent '");
errMsg.append(parentName);
errMsg.append("' can be present.");
dupes.add(parentName);
isInvalid = true;
}
if (artifactId.getName().equals(parentName) && artifactId.getNamespace().toEntityId().getNamespace().equals(parent.getNamespace())) {
throw new InvalidArtifactException(String.format("Invalid parent '%s' for artifact '%s'. An artifact cannot extend itself.", parent, artifactId));
}
}
// "Invalid parents. Only one version range for parent 'etlbatch' can be present."
if (isInvalid) {
throw new InvalidArtifactException(errMsg.toString());
}
}
use of io.cdap.cdap.api.artifact.ArtifactRange 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.api.artifact.ArtifactRange in project cdap by caskdata.
the class ArtifactStore method deleteMeta.
private void deleteMeta(StructuredTableContext context, Id.Artifact artifactId, ArtifactData oldMeta) throws IOException {
// delete old artifact data
StructuredTable artifactTable = getTable(context, StoreDefinition.ArtifactStore.ARTIFACT_DATA_TABLE);
ArtifactCell artifactCell = new ArtifactCell(artifactId);
artifactTable.delete(artifactCell.keys);
// delete old appclass metadata
StructuredTable appClassTable = getTable(context, StoreDefinition.ArtifactStore.APP_DATA_TABLE);
for (ApplicationClass appClass : oldMeta.meta.getClasses().getApps()) {
AppClassKey appClassKey = new AppClassKey(artifactId.getNamespace().toEntityId(), appClass.getClassName());
deleteRangeFromTable(appClassTable, Range.singleton(appClassKey.keys));
}
// delete old plugins, we loop twice to only access to one table at a time to prevent deadlock
StructuredTable pluginDataTable = getTable(context, StoreDefinition.ArtifactStore.PLUGIN_DATA_TABLE);
for (PluginClass pluginClass : oldMeta.meta.getClasses().getPlugins()) {
// delete metadata for each artifact this plugin extends
for (ArtifactRange artifactRange : oldMeta.meta.getUsableBy()) {
// these four fields are prefixes of the plugin table primary keys
PluginKeyPrefix pluginKey = new PluginKeyPrefix(artifactRange.getNamespace(), artifactRange.getName(), pluginClass.getType(), pluginClass.getName());
pluginDataTable.delete(concatFields(pluginKey.keys, artifactCell.keys));
}
}
// Delete the universal plugin row
StructuredTable uniPluginTable = getTable(context, StoreDefinition.ArtifactStore.UNIV_PLUGIN_DATA_TABLE);
for (PluginClass pluginClass : oldMeta.meta.getClasses().getPlugins()) {
if (oldMeta.meta.getUsableBy().isEmpty()) {
UniversalPluginKeyPrefix pluginKey = new UniversalPluginKeyPrefix(artifactId.getNamespace().getId(), pluginClass.getType(), pluginClass.getName());
uniPluginTable.delete(concatFields(pluginKey.keys, artifactCell.keys));
}
}
// delete the old jar file
try {
new EntityImpersonator(artifactId.toEntityId(), impersonator).impersonate(() -> {
Locations.getLocationFromAbsolutePath(locationFactory, oldMeta.getLocationPath()).delete();
return null;
});
} catch (IOException ioe) {
throw ioe;
} catch (Exception e) {
// this should not happen
throw Throwables.propagate(e);
}
}
use of io.cdap.cdap.api.artifact.ArtifactRange in project cdap by caskdata.
the class ArtifactStore method writeMeta.
// write a new artifact snapshot and clean up the old snapshot data
private void writeMeta(StructuredTableContext context, Id.Artifact artifactId, ArtifactData data) throws IOException {
// write to artifact data table
StructuredTable artifactDataTable = getTable(context, StoreDefinition.ArtifactStore.ARTIFACT_DATA_TABLE);
ArtifactCell artifactCell = new ArtifactCell(artifactId);
artifactDataTable.upsert(concatFields(artifactCell.keys, Collections.singleton(Fields.stringField(StoreDefinition.ArtifactStore.ARTIFACT_DATA_FIELD, GSON.toJson(data)))));
ArtifactClasses classes = data.meta.getClasses();
Location artifactLocation = Locations.getLocationFromAbsolutePath(locationFactory, data.getLocationPath());
// write appClass metadata
StructuredTable appTable = getTable(context, StoreDefinition.ArtifactStore.APP_DATA_TABLE);
ArtifactCell artifactkeys = new ArtifactCell(artifactId);
for (ApplicationClass appClass : classes.getApps()) {
// a:{namespace}:{classname}
AppClassKey appClassKey = new AppClassKey(artifactId.getNamespace().toEntityId(), appClass.getClassName());
Field<String> appDataField = Fields.stringField(StoreDefinition.ArtifactStore.APP_DATA_FIELD, GSON.toJson(new AppData(appClass, artifactLocation)));
appTable.upsert(concatFields(appClassKey.keys, artifactkeys.keys, Collections.singleton(appDataField)));
}
// write pluginClass metadata, we loop twice to only access to one table at a time to prevent deadlock
StructuredTable pluginTable = getTable(context, StoreDefinition.ArtifactStore.PLUGIN_DATA_TABLE);
for (PluginClass pluginClass : classes.getPlugins()) {
// write metadata for each artifact this plugin extends
for (ArtifactRange artifactRange : data.meta.getUsableBy()) {
PluginKeyPrefix pluginKey = new PluginKeyPrefix(artifactRange.getNamespace(), artifactRange.getName(), pluginClass.getType(), pluginClass.getName());
Field<String> pluginDataField = Fields.stringField(StoreDefinition.ArtifactStore.PLUGIN_DATA_FIELD, GSON.toJson(new PluginData(pluginClass, artifactLocation, artifactRange)));
pluginTable.upsert(concatFields(pluginKey.keys, artifactkeys.keys, Collections.singleton(pluginDataField)));
}
}
// write universal plugin class metadata
StructuredTable uniPluginTable = getTable(context, StoreDefinition.ArtifactStore.UNIV_PLUGIN_DATA_TABLE);
for (PluginClass pluginClass : classes.getPlugins()) {
// by any other artifact in the same namespace.
if (data.meta.getUsableBy().isEmpty()) {
// Write a special entry for plugin that doesn't have parent, which means any artifact can use it
UniversalPluginKeyPrefix pluginKey = new UniversalPluginKeyPrefix(artifactId.getNamespace().getId(), pluginClass.getType(), pluginClass.getName());
Field<String> pluginDataField = Fields.stringField(StoreDefinition.ArtifactStore.PLUGIN_DATA_FIELD, GSON.toJson(new PluginData(pluginClass, artifactLocation, null)));
uniPluginTable.upsert(concatFields(pluginKey.keys, artifactkeys.keys, Collections.singleton(pluginDataField)));
}
}
}
use of io.cdap.cdap.api.artifact.ArtifactRange in project cdap by caskdata.
the class ArtifactHttpHandler method getArtifactProperties.
@POST
@Path("/namespaces/{namespace-id}/artifactproperties")
public void getArtifactProperties(FullHttpRequest request, HttpResponder responder, @PathParam("namespace-id") String namespaceId, @QueryParam("order") @DefaultValue("DESC") String order) throws Exception {
NamespaceId namespace = validateAndGetNamespace(namespaceId);
ArtifactSortOrder sortOrder = ArtifactSortOrder.valueOf(order);
List<ArtifactPropertiesRequest> propertyRequests;
try (Reader reader = new InputStreamReader(new ByteBufInputStream(request.content()), StandardCharsets.UTF_8)) {
propertyRequests = GSON.fromJson(reader, BATCH_ARTIFACT_PROPERTIES_REQUEST);
} catch (JsonSyntaxException e) {
throw new BadRequestException("Unable to parse request: " + e.getMessage(), e);
}
List<ArtifactSummaryProperties> result = new ArrayList<>(propertyRequests.size());
for (ArtifactPropertiesRequest propertiesRequest : propertyRequests) {
NamespaceId requestNamespace = propertiesRequest.getScope() == ArtifactScope.SYSTEM ? NamespaceId.SYSTEM : namespace;
ArtifactRange range = new ArtifactRange(requestNamespace.getNamespace(), propertiesRequest.getName(), ArtifactVersionRange.parse(propertiesRequest.getVersion()));
List<ArtifactDetail> artifactDetails = artifactRepository.getArtifactDetails(range, 1, sortOrder);
for (ArtifactDetail artifactDetail : artifactDetails) {
Map<String, String> properties = artifactDetail.getMeta().getProperties();
Map<String, String> filteredProperties = new HashMap<>(propertiesRequest.getProperties().size());
for (String propertyKey : propertiesRequest.getProperties()) {
if (properties.containsKey(propertyKey)) {
filteredProperties.put(propertyKey, properties.get(propertyKey));
}
}
String artifactVersion = artifactDetail.getDescriptor().getArtifactId().getVersion().getVersion();
result.add(new ArtifactSummaryProperties(propertiesRequest.getName(), artifactVersion, propertiesRequest.getScope(), filteredProperties));
}
}
responder.sendJson(HttpResponseStatus.OK, GSON.toJson(result, BATCH_ARTIFACT_PROPERTIES_RESPONSE));
}
Aggregations