use of io.cdap.cdap.internal.app.runtime.plugin.PluginNotExistsException 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<io.cdap.cdap.proto.id.ArtifactId> pluginRange, int limit, ArtifactSortOrder order) throws IOException, ArtifactNotFoundException, PluginNotExistsException {
SortedMap<ArtifactDescriptor, PluginClass> result = TransactionRunners.run(transactionRunner, context -> {
StructuredTable artifactDataTable = getTable(context, StoreDefinition.ArtifactStore.ARTIFACT_DATA_TABLE);
List<ArtifactDetail> parentArtifactDetails = getArtifacts(artifactDataTable, 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) && isAllowed(pluginClass)) {
plugins.put(parentArtifactDetail.getDescriptor(), pluginClass);
break;
}
}
}
// Add all plugins that extends from the given set of parents
StructuredTable pluginTable = getTable(context, StoreDefinition.ArtifactStore.PLUGIN_DATA_TABLE);
PluginKeyPrefix pluginKey = new PluginKeyPrefix(parentArtifactRange.getNamespace(), parentArtifactRange.getName(), type, name);
try (CloseableIterator<StructuredRow> iterator = pluginTable.scan(Range.singleton(pluginKey.keys), Integer.MAX_VALUE)) {
addPluginsInRangeToMap(namespace, parentArtifacts, iterator, plugins, pluginRange, limit);
}
// Add all universal plugins
StructuredTable uniPluginTable = getTable(context, StoreDefinition.ArtifactStore.UNIV_PLUGIN_DATA_TABLE);
for (String ns : Arrays.asList(namespace.getNamespace(), NamespaceId.SYSTEM.getNamespace())) {
UniversalPluginKeyPrefix universalPluginKey = new UniversalPluginKeyPrefix(ns, type, name);
try (CloseableIterator<StructuredRow> iterator = uniPluginTable.scan(Range.singleton(universalPluginKey.keys), Integer.MAX_VALUE)) {
addPluginsInRangeToMap(namespace, parentArtifacts, iterator, 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 io.cdap.cdap.internal.app.runtime.plugin.PluginNotExistsException in project cdap by caskdata.
the class DefaultPluginConfigurer method addPlugin.
protected Plugin addPlugin(String pluginType, String pluginName, String pluginId, PluginProperties properties, PluginSelector selector) throws PluginNotExistsException {
validateExistingPlugin(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.
io.cdap.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, 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 io.cdap.cdap.internal.app.runtime.plugin.PluginNotExistsException in project cdap by caskdata.
the class RemotePluginFinder method getPlugins.
/**
* Gets a list of {@link PluginInfo} from the artifact extension endpoint.
*
* @param namespaceId namespace of the call happening in
* @param parentArtifactId the parent artifact id
* @param pluginType the plugin type to look for
* @param pluginName the plugin name to look for
* @return a list of {@link PluginInfo}
* @throws IOException if it failed to get the information
* @throws PluginNotExistsException if the given plugin type and name doesn't exist
*/
private List<PluginInfo> getPlugins(NamespaceId namespaceId, ArtifactId parentArtifactId, String pluginType, String pluginName) throws IOException, PluginNotExistsException, UnauthorizedException {
// replace the space in the name
// TODO: CDAP-18375 improve url encoding the our remote call
pluginName = pluginName.replace(" ", "%20");
HttpRequest.Builder requestBuilder = remoteClient.requestBuilder(HttpMethod.GET, String.format("namespaces/%s/artifacts/%s/versions/%s/extensions/%s/plugins/%s?scope=%s&pluginScope=%s", namespaceId.getNamespace(), parentArtifactId.getArtifact(), parentArtifactId.getVersion(), pluginType, pluginName, NamespaceId.SYSTEM.equals(parentArtifactId.getNamespaceId()) ? ArtifactScope.SYSTEM : ArtifactScope.USER, NamespaceId.SYSTEM.equals(namespaceId.getNamespaceId()) ? ArtifactScope.SYSTEM : ArtifactScope.USER));
HttpResponse response = remoteClient.execute(requestBuilder.build());
int responseCode = response.getResponseCode();
if (responseCode != HttpURLConnection.HTTP_OK) {
switch(responseCode) {
// throw retryable error if app fabric is not available, might be due to restarting
case HttpURLConnection.HTTP_BAD_GATEWAY:
case HttpURLConnection.HTTP_UNAVAILABLE:
case HttpURLConnection.HTTP_GATEWAY_TIMEOUT:
throw new ServiceUnavailableException(Constants.Service.APP_FABRIC_HTTP, Constants.Service.APP_FABRIC_HTTP + " service is not available with status " + responseCode);
case HttpURLConnection.HTTP_NOT_FOUND:
throw new PluginNotExistsException(namespaceId, pluginType, pluginName);
}
throw new IllegalArgumentException("Failure in getting plugin information with type " + pluginType + " and name " + pluginName + " that extends " + parentArtifactId + ". Reason is " + responseCode + ": " + response.getResponseBodyAsString());
}
return GSON.fromJson(response.getResponseBodyAsString(), PLUGIN_INFO_LIST_TYPE);
}
use of io.cdap.cdap.internal.app.runtime.plugin.PluginNotExistsException in project cdap by caskdata.
the class ArtifactStoreTest method testSnapshotMutability.
@Test
public void testSnapshotMutability() throws Exception {
// write parent
Id.Artifact parentArtifactId = Id.Artifact.from(Id.Namespace.DEFAULT, "parent", "1.0.0");
ArtifactMeta parentMeta = new ArtifactMeta(ArtifactClasses.builder().build());
writeArtifact(parentArtifactId, parentMeta, "content");
ArtifactRange parentArtifacts = new ArtifactRange(NamespaceId.DEFAULT.getNamespace(), "parent", new ArtifactVersion("1.0.0"), new ArtifactVersion("2.0.0"));
// write the snapshot once
PluginClass plugin1 = PluginClass.builder().setName("plugin1").setType("atype").setDescription("").setClassName("c.c.c.plugin1").setConfigFieldName("cfg").setProperties(ImmutableMap.of()).build();
PluginClass plugin2 = PluginClass.builder().setName("plugin2").setType("atype").setDescription("").setClassName("c.c.c.plugin2").setConfigFieldName("cfg").setProperties(ImmutableMap.of()).build();
Id.Artifact artifactId = Id.Artifact.from(Id.Namespace.DEFAULT, "myplugins", "1.0.0-SNAPSHOT");
ArtifactMeta artifactMeta = new ArtifactMeta(ArtifactClasses.builder().addPlugins(plugin1, plugin2).build(), ImmutableSet.of(parentArtifacts));
writeArtifact(artifactId, artifactMeta, "abc123");
// update meta and jar contents
artifactMeta = new ArtifactMeta(ArtifactClasses.builder().addPlugin(plugin2).build(), ImmutableSet.of(parentArtifacts));
writeArtifact(artifactId, artifactMeta, "xyz321");
// check the metadata and contents got updated
ArtifactDetail detail = artifactStore.getArtifact(artifactId);
assertEqual(artifactId, artifactMeta, "xyz321", detail);
// check that plugin1 was deleted and plugin2 remains
Assert.assertEquals(ImmutableMap.of(detail.getDescriptor(), plugin2), artifactStore.getPluginClasses(NamespaceId.DEFAULT, parentArtifactId, plugin2.getType(), plugin2.getName(), null, Integer.MAX_VALUE, ArtifactSortOrder.UNORDERED));
try {
artifactStore.getPluginClasses(NamespaceId.DEFAULT, parentArtifactId, plugin1.getType(), plugin1.getName(), null, Integer.MAX_VALUE, ArtifactSortOrder.UNORDERED);
Assert.fail();
} catch (PluginNotExistsException e) {
// expected
}
}
use of io.cdap.cdap.internal.app.runtime.plugin.PluginNotExistsException in project cdap by caskdata.
the class ArtifactRepositoryTest method testPluginSelector.
@Test
public void testPluginSelector() throws Exception {
// No plugin yet
ArtifactRange appArtifactRange = new ArtifactRange(APP_ARTIFACT_ID.getNamespace().getId(), APP_ARTIFACT_ID.getName(), APP_ARTIFACT_ID.getVersion(), true, APP_ARTIFACT_ID.getVersion(), true);
try {
artifactRepository.findPlugin(NamespaceId.DEFAULT, appArtifactRange, "plugin", "TestPlugin2", new PluginSelector());
Assert.fail();
} catch (PluginNotExistsException e) {
// expected
}
File pluginDir = DirUtils.createTempDir(tmpDir);
// Create a plugin jar. It contains two plugins, TestPlugin and TestPlugin2 inside.
Id.Artifact artifact1Id = Id.Artifact.from(Id.Namespace.DEFAULT, "myPlugin", "1.0");
Manifest manifest = createManifest(ManifestFields.EXPORT_PACKAGE, TestPlugin.class.getPackage().getName());
File jarFile = createPluginJar(TestPlugin.class, new File(tmpDir, "myPlugin-1.0.jar"), manifest);
// Build up the plugin repository.
Set<ArtifactRange> parents = ImmutableSet.of(new ArtifactRange(APP_ARTIFACT_ID.getNamespace().getId(), APP_ARTIFACT_ID.getName(), new ArtifactVersion("1.0.0"), new ArtifactVersion("2.0.0")));
artifactRepository.addArtifact(artifact1Id, jarFile, parents, null);
// Should get the only version.
Map.Entry<ArtifactDescriptor, PluginClass> plugin = artifactRepository.findPlugin(NamespaceId.DEFAULT, appArtifactRange, "plugin", "TestPlugin2", new PluginSelector());
Assert.assertNotNull(plugin);
Assert.assertEquals(new ArtifactVersion("1.0"), plugin.getKey().getArtifactId().getVersion());
Assert.assertEquals("TestPlugin2", plugin.getValue().getName());
Locations.linkOrCopyOverwrite(plugin.getKey().getLocation(), new File(pluginDir, Artifacts.getFileName(plugin.getKey().getArtifactId())));
// Create another plugin jar with later version and update the repository
Id.Artifact artifact2Id = Id.Artifact.from(Id.Namespace.DEFAULT, "myPlugin", "2.0");
jarFile = createPluginJar(TestPlugin.class, new File(tmpDir, "myPlugin-2.0.jar"), manifest);
artifactRepository.addArtifact(artifact2Id, jarFile, parents, null);
// Should select the latest version
plugin = artifactRepository.findPlugin(NamespaceId.DEFAULT, appArtifactRange, "plugin", "TestPlugin2", new PluginSelector());
Assert.assertNotNull(plugin);
Assert.assertEquals(new ArtifactVersion("2.0"), plugin.getKey().getArtifactId().getVersion());
Assert.assertEquals("TestPlugin2", plugin.getValue().getName());
Locations.linkOrCopyOverwrite(plugin.getKey().getLocation(), new File(pluginDir, Artifacts.getFileName(plugin.getKey().getArtifactId())));
// Load the Plugin class from the classLoader.
try (PluginInstantiator instantiator = new PluginInstantiator(cConf, appClassLoader, pluginDir)) {
ClassLoader pluginClassLoader = instantiator.getArtifactClassLoader(plugin.getKey().getArtifactId());
Class<?> pluginClass = pluginClassLoader.loadClass(TestPlugin2.class.getName());
// Use a custom plugin selector to select with smallest version
plugin = artifactRepository.findPlugin(NamespaceId.DEFAULT, appArtifactRange, "plugin", "TestPlugin2", new PluginSelector() {
@Override
public Map.Entry<ArtifactId, PluginClass> select(SortedMap<ArtifactId, PluginClass> plugins) {
return plugins.entrySet().iterator().next();
}
});
Assert.assertNotNull(plugin);
Assert.assertEquals(new ArtifactVersion("1.0"), plugin.getKey().getArtifactId().getVersion());
Assert.assertEquals("TestPlugin2", plugin.getValue().getName());
// Load the Plugin class again from the current plugin selected
// The plugin class should be different (from different ClassLoader)
// The empty class should be the same (from the plugin lib ClassLoader)
pluginClassLoader = instantiator.getArtifactClassLoader(plugin.getKey().getArtifactId());
Assert.assertNotSame(pluginClass, pluginClassLoader.loadClass(TestPlugin2.class.getName()));
// From the pluginClassLoader, loading export classes from the template jar should be allowed
Class<?> cls = pluginClassLoader.loadClass(PluginTestRunnable.class.getName());
// The class should be loaded from the parent artifact's classloader
Assert.assertSame(appClassLoader.loadClass(PluginTestRunnable.class.getName()), cls);
// From the plugin classloader, all cdap api classes is loadable as well.
cls = pluginClassLoader.loadClass(Application.class.getName());
// The Application class should be the same as the one in the system classloader
Assert.assertSame(Application.class, cls);
}
}
Aggregations