use of co.cask.cdap.api.plugin.PluginClass 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;
}
}
use of co.cask.cdap.api.plugin.PluginClass in project cdap by caskdata.
the class DefaultPluginConfigurer method addPlugin.
private Plugin addPlugin(String pluginType, String pluginName, String pluginId, PluginProperties properties, PluginSelector selector) throws PluginNotExistsException {
PluginWithLocation existing = plugins.get(pluginId);
if (existing != null) {
throw new IllegalArgumentException(String.format("Plugin of type %s, name %s was already added as id %s.", existing.getPlugin().getPluginClass().getType(), existing.getPlugin().getPluginClass().getName(), pluginId));
}
final Class[] callerClasses = CallerClassSecurityManager.getCallerClasses();
if (callerClasses.length < 3) {
// This shouldn't happen as there should be someone calling this method.
throw new IllegalStateException("Invalid call stack.");
}
Set<ArtifactId> parents = new LinkedHashSet<>();
// 0 is the CallerClassSecurityManager, 1 is this class, hence 2 is the actual caller
for (int i = 2; i < callerClasses.length; i++) {
ClassLoader classloader = callerClasses[i].getClassLoader();
if (classloader instanceof PluginClassLoader) {
// if this is the first time we've seen this plugin artifact, it must be a new plugin parent.
co.cask.cdap.api.artifact.ArtifactId pluginCallerArtifactId = ((PluginClassLoader) classloader).getArtifactId();
parents.add((pluginCallerArtifactId.getScope() == ArtifactScope.SYSTEM ? NamespaceId.SYSTEM : pluginNamespaceId).artifact(pluginCallerArtifactId.getName(), pluginCallerArtifactId.getVersion().getVersion()));
}
}
PluginNotExistsException exception = null;
for (ArtifactId parentId : Iterables.concat(parents, Collections.singleton(artifactId))) {
try {
Map.Entry<ArtifactDescriptor, PluginClass> pluginEntry = pluginFinder.findPlugin(pluginNamespaceId, parentId, pluginType, pluginName, selector);
Plugin plugin = FindPluginHelper.getPlugin(Iterables.transform(parents, ArtifactId::toApiArtifactId), pluginEntry, properties, pluginType, pluginName, pluginInstantiator);
plugins.put(pluginId, new PluginWithLocation(plugin, pluginEntry.getKey().getLocation()));
return plugin;
} catch (PluginNotExistsException e) {
// ignore this in case the plugin extends something higher up in the call stack.
// For example, suppose the app uses pluginA, which uses pluginB. However, pluginB is a plugin that
// has the app as its parent and not pluginA as its parent. In this case, we want to keep going up the call
// stack until we get to the app as the parent, which would be able to find plugin B.
exception = e;
}
}
throw exception == null ? new PluginNotExistsException(pluginNamespaceId, pluginType, pluginName) : exception;
}
use of co.cask.cdap.api.plugin.PluginClass in project cdap by caskdata.
the class ArtifactStore method deleteMeta.
private void deleteMeta(Table table, Id.Artifact artifactId, byte[] oldData) throws IOException {
// delete old artifact data
ArtifactCell artifactCell = new ArtifactCell(artifactId);
table.delete(artifactCell.rowkey, artifactCell.column);
// delete old plugins
final ArtifactData oldMeta = GSON.fromJson(Bytes.toString(oldData), ArtifactData.class);
byte[] artifactColumn = new ArtifactColumn(artifactId).getColumn();
for (PluginClass pluginClass : oldMeta.meta.getClasses().getPlugins()) {
// delete metadata for each artifact this plugin extends
for (ArtifactRange artifactRange : oldMeta.meta.getUsableBy()) {
// p:{namespace}:{type}:{name}
PluginKey pluginKey = new PluginKey(artifactRange.getNamespace(), artifactRange.getName(), pluginClass.getType(), pluginClass.getName());
table.delete(pluginKey.getRowKey(), artifactColumn);
}
// Delete the universal plugin row
if (oldMeta.meta.getUsableBy().isEmpty()) {
UniversalPluginKey pluginKey = new UniversalPluginKey(artifactId.getNamespace().getId(), pluginClass.getType(), pluginClass.getName());
table.delete(pluginKey.getRowKey(), artifactColumn);
}
}
// delete old appclass metadata
for (ApplicationClass appClass : oldMeta.meta.getClasses().getApps()) {
AppClassKey appClassKey = new AppClassKey(artifactId.getNamespace().toEntityId(), appClass.getClassName());
table.delete(appClassKey.getRowKey(), artifactColumn);
}
try {
new EntityImpersonator(artifactId.toEntityId(), impersonator).impersonate(new Callable<Void>() {
@Override
public Void call() throws Exception {
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 co.cask.cdap.api.plugin.PluginClass in project cdap by caskdata.
the class ArtifactStore method getPluginClasses.
/**
* Get all plugin classes of the given type and name that extend the given parent artifact.
* Results are returned as a map from plugin artifact to plugins in that artifact.
*
* @param namespace the namespace to search for plugins. The system namespace is always included.
* @param parentArtifactRange the parent artifact range to find plugins for
* @param type the type of plugin to look for
* @param name the name of the plugin to look for
* @param pluginRange the predicate for the plugins
* @param limit the limit number of the result
* @param order the order of the result
* @return an unmodifiable map of plugin artifact to plugin classes of the given type and name, accessible by the
* given artifact. The map will never be null, and will never be empty.
* @throws PluginNotExistsException if no plugin with the given type and name exists in the namespace
* @throws IOException if there was an exception reading metadata from the metastore
*/
public SortedMap<ArtifactDescriptor, PluginClass> getPluginClasses(NamespaceId namespace, ArtifactRange parentArtifactRange, String type, String name, @Nullable final Predicate<co.cask.cdap.proto.id.ArtifactId> pluginRange, int limit, ArtifactSortOrder order) throws IOException, ArtifactNotFoundException, PluginNotExistsException {
SortedMap<ArtifactDescriptor, PluginClass> result = Transactionals.execute(transactional, context -> {
Table metaTable = getMetaTable(context);
List<ArtifactDetail> parentArtifactDetails = getArtifacts(metaTable, parentArtifactRange, Integer.MAX_VALUE, null);
if (parentArtifactDetails.isEmpty()) {
throw new ArtifactNotFoundException(parentArtifactRange.getNamespace(), parentArtifactRange.getName());
}
SortedMap<ArtifactDescriptor, PluginClass> plugins = order == ArtifactSortOrder.DESC ? new TreeMap<>(Collections.reverseOrder()) : new TreeMap<>();
List<Id.Artifact> parentArtifacts = new ArrayList<>();
for (ArtifactDetail parentArtifactDetail : parentArtifactDetails) {
parentArtifacts.add(Id.Artifact.from(Id.Namespace.from(parentArtifactRange.getNamespace()), parentArtifactDetail.getDescriptor().getArtifactId()));
Set<PluginClass> parentPlugins = parentArtifactDetail.getMeta().getClasses().getPlugins();
for (PluginClass pluginClass : parentPlugins) {
if (pluginClass.getName().equals(name) && pluginClass.getType().equals(type)) {
plugins.put(parentArtifactDetail.getDescriptor(), pluginClass);
break;
}
}
}
// Add all plugins that extends from the given set of parents
PluginKey pluginKey = new PluginKey(parentArtifactRange.getNamespace(), parentArtifactRange.getName(), type, name);
Row row = metaTable.get(pluginKey.getRowKey());
if (!row.isEmpty()) {
addPluginsInRangeToMap(namespace, parentArtifacts, row.getColumns(), plugins, pluginRange, limit);
}
// Add all universal plugins
for (String ns : Arrays.asList(namespace.getNamespace(), NamespaceId.SYSTEM.getNamespace())) {
UniversalPluginKey universalPluginKey = new UniversalPluginKey(ns, type, name);
row = metaTable.get(universalPluginKey.getRowKey());
if (!row.isEmpty()) {
addPluginsInRangeToMap(namespace, parentArtifacts, row.getColumns(), plugins, pluginRange, limit);
}
}
return Collections.unmodifiableSortedMap(plugins);
}, IOException.class, ArtifactNotFoundException.class);
if (result.isEmpty()) {
throw new PluginNotExistsException(new NamespaceId(parentArtifactRange.getNamespace()), type, name);
}
return result;
}
use of co.cask.cdap.api.plugin.PluginClass in project cdap by caskdata.
the class PluginService method getPluginEndpoint.
private PluginEndpoint getPluginEndpoint(NamespaceId namespace, ArtifactDetail artifactDetail, String pluginType, String pluginName, ArtifactDescriptor parentArtifactDescriptor, Set<ArtifactRange> parentArtifactRanges, String methodName) throws NotFoundException, IOException, ClassNotFoundException {
Id.Artifact artifactId = Id.Artifact.from(Id.Namespace.fromEntityId(namespace), artifactDetail.getDescriptor().getArtifactId());
Set<PluginClass> pluginClasses = artifactDetail.getMeta().getClasses().getPlugins();
PluginClass pluginClass = null;
for (PluginClass plugin : pluginClasses) {
if (plugin.getName().equals(pluginName) && plugin.getType().equals(pluginType)) {
// plugin type and name matched, next check for endpoint method presence
if (plugin.getEndpoints() == null || !plugin.getEndpoints().contains(methodName)) {
throw new NotFoundException(String.format("Plugin with type: %s name: %s found, " + "but Endpoint %s was not found", pluginType, pluginName, methodName));
}
pluginClass = plugin;
}
}
if (pluginClass == null) {
throw new NotFoundException(String.format("No Plugin with type : %s, name: %s was found", pluginType, pluginName));
}
// initialize parent classloader and plugin instantiator
Instantiators instantiators = this.instantiators.getUnchecked(parentArtifactDescriptor);
PluginInstantiator pluginInstantiator = instantiators.getPluginInstantiator(artifactDetail, artifactId.toArtifactId());
// we pass the parent artifact to endpoint plugin context,
// as plugin method will use this context to load other plugins.
DefaultEndpointPluginContext defaultEndpointPluginContext = new DefaultEndpointPluginContext(namespace, authArtifactRepository, pluginInstantiator, parentArtifactRanges);
return getPluginEndpoint(pluginInstantiator, artifactId, pluginClass, methodName, defaultEndpointPluginContext);
}
Aggregations