use of co.cask.cdap.api.plugin.PluginClass in project cdap by caskdata.
the class PluginService method getPluginEndpoint.
/**
* load and instantiate the plugin and return {@link PluginEndpoint}
* which can be used to invoke plugin method with request object.
*
* @param pluginInstantiator to instantiate plugin instances.
* @param artifact artifact of the plugin
* @param pluginClass class having the plugin method to invoke
* @param endpointName name of the endpoint to invoke
* @param endpointPluginContext endpoint plugin context that can optionally be passed to the method
* @throws IOException if there was an exception getting the classloader
* @throws ClassNotFoundException if plugin class cannot be loaded
* @throws NotFoundException Not Found exception thrown if no matching plugin found.
*/
private PluginEndpoint getPluginEndpoint(final PluginInstantiator pluginInstantiator, Id.Artifact artifact, PluginClass pluginClass, String endpointName, final DefaultEndpointPluginContext endpointPluginContext) throws IOException, ClassNotFoundException, NotFoundException {
Plugin plugin = new Plugin(new ArrayList<ArtifactId>(), artifact.toArtifactId(), pluginClass, PluginProperties.builder().build());
ClassLoader pluginClassLoader = pluginInstantiator.getPluginClassLoader(plugin);
Class pluginClassLoaded = pluginClassLoader.loadClass(pluginClass.getClassName());
final Object pluginInstance = pluginInstantiator.newInstanceWithoutConfig(plugin);
Method[] methods = pluginClassLoaded.getMethods();
for (final Method method : methods) {
Path pathAnnotation = method.getAnnotation(Path.class);
// method should have path annotation else continue
if (pathAnnotation == null || !pathAnnotation.value().equals(endpointName)) {
continue;
}
return new PluginEndpoint() {
@Override
public java.lang.reflect.Type getMethodParameterType() throws IllegalArgumentException {
if (method.getParameterTypes().length == 0) {
// should not happen, checks should have happened during deploy artifact.
throw new IllegalArgumentException("No Method parameter type found");
}
return method.getGenericParameterTypes()[0];
}
@Override
public java.lang.reflect.Type getResultType() {
return method.getGenericReturnType();
}
@Override
public Object invoke(Object request) throws IOException, ClassNotFoundException, InvocationTargetException, IllegalAccessException, IllegalArgumentException {
if (method.getParameterTypes().length == 2 && EndpointPluginContext.class.isAssignableFrom(method.getParameterTypes()[1])) {
return method.invoke(pluginInstance, request, endpointPluginContext);
} else if (method.getParameterTypes().length == 1) {
return method.invoke(pluginInstance, request);
} else {
throw new IllegalArgumentException(String.format("Only method with 1 parameter and optional EndpointPluginContext as 2nd parameter is " + "allowed in Plugin endpoint method, Found %s parameters", method.getParameterTypes().length));
}
}
};
}
;
// cannot find the endpoint in plugin method. should not happen as this is checked earlier.
throw new NotFoundException("Could not find the plugin method with the requested method endpoint {}", endpointName);
}
use of co.cask.cdap.api.plugin.PluginClass in project cdap by caskdata.
the class ArtifactRepositoryTest method testPlugin.
@Test
public void testPlugin() throws Exception {
File pluginDir = TMP_FOLDER.newFolder();
addPluginArtifact();
SortedMap<ArtifactDescriptor, Set<PluginClass>> plugins = getPlugins();
copyArtifacts(pluginDir, plugins);
// Instantiate the plugins and execute them
try (PluginInstantiator instantiator = new PluginInstantiator(cConf, appClassLoader, pluginDir)) {
for (Map.Entry<ArtifactDescriptor, Set<PluginClass>> entry : plugins.entrySet()) {
for (PluginClass pluginClass : entry.getValue()) {
Plugin pluginInfo = new Plugin(new ArrayList<ArtifactId>(), entry.getKey().getArtifactId(), pluginClass, PluginProperties.builder().add("class.name", TEST_EMPTY_CLASS).add("nullableLongFlag", "10").add("host", "example.com").add("aBoolean", "${aBoolean}").add("aByte", "${aByte}").add("aChar", "${aChar}").add("aDouble", "${aDouble}").add("anInt", "${anInt}").add("aFloat", "${aFloat}").add("aLong", "${aLong}").add("aShort", "${aShort}").build());
Callable<String> plugin = instantiator.newInstance(pluginInfo);
Assert.assertEquals("example.com,false,0,\u0000,0.0,0.0,0,0,0", plugin.call());
}
}
}
}
use of co.cask.cdap.api.plugin.PluginClass in project cdap by caskdata.
the class ArtifactRepositoryTest method testPluginConfigWithNoStringValues.
@Test
public void testPluginConfigWithNoStringValues() throws Exception {
File pluginDir = TMP_FOLDER.newFolder();
addPluginArtifact();
SortedMap<ArtifactDescriptor, Set<PluginClass>> plugins = getPlugins();
copyArtifacts(pluginDir, plugins);
String numericValue = "42";
// Instantiate the plugins and execute them
try (PluginInstantiator instantiator = new PluginInstantiator(cConf, appClassLoader, pluginDir)) {
for (Map.Entry<ArtifactDescriptor, Set<PluginClass>> entry : plugins.entrySet()) {
for (PluginClass pluginClass : entry.getValue()) {
Plugin pluginInfo = new Plugin(new ArrayList<ArtifactId>(), entry.getKey().getArtifactId(), pluginClass, PluginProperties.builder().add("class.name", TEST_EMPTY_CLASS).add("nullableLongFlag", numericValue).add("host", "example.com").add("aBoolean", "${aBoolean}").add("aByte", numericValue).add("aChar", "${aChar}").add("aDouble", "${aDouble}").add("anInt", numericValue).add("aFloat", "${aFloat}").add("aLong", numericValue).add("aShort", numericValue).build());
// first test with quotes ("42")
String jsonPluginStr = GSON.toJson(pluginInfo);
pluginInfo = GSON.fromJson(jsonPluginStr, Plugin.class);
instantiator.newInstance(pluginInfo);
// test without quotes (42)
pluginInfo = GSON.fromJson(jsonPluginStr.replaceAll("\"" + numericValue + "\"", numericValue), Plugin.class);
instantiator.newInstance(pluginInfo);
// test with quotes and dot ("42.0")
pluginInfo = GSON.fromJson(jsonPluginStr.replaceAll(numericValue, numericValue + ".0"), Plugin.class);
instantiator.newInstance(pluginInfo);
// test with dot (42.0)
pluginInfo = GSON.fromJson(jsonPluginStr.replaceAll("\"" + numericValue + "\"", numericValue + ".0"), Plugin.class);
instantiator.newInstance(pluginInfo);
// test with some actual double number 42.5
pluginInfo = GSON.fromJson(jsonPluginStr.replaceAll("\"" + numericValue + "\"", numericValue + ".5"), Plugin.class);
try {
instantiator.newInstance(pluginInfo);
Assert.fail("Plugin instantiation should fail with value '42.5'");
} catch (InvalidPluginConfigException e) {
// expected
}
}
}
}
}
use of co.cask.cdap.api.plugin.PluginClass in project cdap by caskdata.
the class ArtifactRepositoryTest method testAddSystemArtifacts.
@Test
public void testAddSystemArtifacts() throws Exception {
Id.Artifact systemAppArtifactId = Id.Artifact.from(Id.Namespace.SYSTEM, "PluginTest", "1.0.0");
File systemAppJar = createAppJar(PluginTestApp.class, new File(systemArtifactsDir1, "PluginTest-1.0.0.jar"), createManifest(ManifestFields.EXPORT_PACKAGE, PluginTestRunnable.class.getPackage().getName()));
// write plugins jar
Id.Artifact pluginArtifactId1 = Id.Artifact.from(Id.Namespace.SYSTEM, "APlugin", "1.0.0");
Manifest manifest = createManifest(ManifestFields.EXPORT_PACKAGE, TestPlugin.class.getPackage().getName());
File pluginJar1 = createPluginJar(TestPlugin.class, new File(systemArtifactsDir1, "APlugin-1.0.0.jar"), manifest);
// write plugins config file
Map<String, PluginPropertyField> emptyMap = Collections.emptyMap();
Set<PluginClass> manuallyAddedPlugins1 = ImmutableSet.of(new PluginClass("typeA", "manual1", "desc", "co.cask.classname", null, emptyMap), new PluginClass("typeB", "manual2", "desc", "co.cask.otherclassname", null, emptyMap));
File pluginConfigFile = new File(systemArtifactsDir1, "APlugin-1.0.0.json");
ArtifactConfig pluginConfig1 = new ArtifactConfig(ImmutableSet.of(new ArtifactRange(NamespaceId.SYSTEM.getNamespace(), "PluginTest", new ArtifactVersion("0.9.0"), new ArtifactVersion("2.0.0"))), // add a dummy plugin to test explicit addition of plugins through the config file
manuallyAddedPlugins1, ImmutableMap.of("k1", "v1", "k2", "v2"));
try (BufferedWriter writer = Files.newWriter(pluginConfigFile, Charsets.UTF_8)) {
writer.write(pluginConfig1.toString());
}
// write another plugins jar to a different directory, to test that plugins will get picked up from both directories
Id.Artifact pluginArtifactId2 = Id.Artifact.from(Id.Namespace.SYSTEM, "BPlugin", "1.0.0");
manifest = createManifest(ManifestFields.EXPORT_PACKAGE, TestPlugin.class.getPackage().getName());
File pluginJar2 = createPluginJar(TestPlugin.class, new File(systemArtifactsDir2, "BPlugin-1.0.0.jar"), manifest);
// write plugins config file
Set<PluginClass> manuallyAddedPlugins2 = ImmutableSet.of(new PluginClass("typeA", "manual1", "desc", "co.notcask.classname", null, emptyMap), new PluginClass("typeB", "manual2", "desc", "co.notcask.otherclassname", null, emptyMap));
pluginConfigFile = new File(systemArtifactsDir2, "BPlugin-1.0.0.json");
ArtifactConfig pluginConfig2 = new ArtifactConfig(ImmutableSet.of(new ArtifactRange(NamespaceId.SYSTEM.getNamespace(), "PluginTest", new ArtifactVersion("0.9.0"), new ArtifactVersion("2.0.0"))), manuallyAddedPlugins2, ImmutableMap.of("k3", "v3"));
try (BufferedWriter writer = Files.newWriter(pluginConfigFile, Charsets.UTF_8)) {
writer.write(pluginConfig2.toString());
}
artifactRepository.addSystemArtifacts();
Assert.assertTrue(systemAppJar.delete());
Assert.assertTrue(pluginJar1.delete());
Assert.assertTrue(pluginJar2.delete());
try {
// check app artifact added correctly
ArtifactDetail appArtifactDetail = artifactRepository.getArtifact(systemAppArtifactId);
Map<ArtifactDescriptor, Set<PluginClass>> plugins = artifactRepository.getPlugins(NamespaceId.DEFAULT, systemAppArtifactId);
Assert.assertEquals(2, plugins.size());
Set<PluginClass> pluginClasses = plugins.values().iterator().next();
Set<String> pluginNames = Sets.newHashSet();
for (PluginClass pluginClass : pluginClasses) {
pluginNames.add(pluginClass.getName());
}
Assert.assertEquals(Sets.newHashSet("manual1", "manual2", "TestPlugin", "TestPlugin2"), pluginNames);
Assert.assertEquals(systemAppArtifactId.getName(), appArtifactDetail.getDescriptor().getArtifactId().getName());
Assert.assertEquals(systemAppArtifactId.getVersion(), appArtifactDetail.getDescriptor().getArtifactId().getVersion());
// check plugin artifact added correctly
ArtifactDetail pluginArtifactDetail = artifactRepository.getArtifact(pluginArtifactId1);
Assert.assertEquals(pluginArtifactId1.getName(), pluginArtifactDetail.getDescriptor().getArtifactId().getName());
Assert.assertEquals(pluginArtifactId1.getVersion(), pluginArtifactDetail.getDescriptor().getArtifactId().getVersion());
// check manually added plugins are there
Assert.assertTrue(pluginArtifactDetail.getMeta().getClasses().getPlugins().containsAll(manuallyAddedPlugins1));
// check properties are there
Assert.assertEquals(pluginConfig1.getProperties(), pluginArtifactDetail.getMeta().getProperties());
// check other plugin artifact added correctly
pluginArtifactDetail = artifactRepository.getArtifact(pluginArtifactId2);
Assert.assertEquals(pluginArtifactId2.getName(), pluginArtifactDetail.getDescriptor().getArtifactId().getName());
Assert.assertEquals(pluginArtifactId2.getVersion(), pluginArtifactDetail.getDescriptor().getArtifactId().getVersion());
// check manually added plugins are there
Assert.assertTrue(pluginArtifactDetail.getMeta().getClasses().getPlugins().containsAll(manuallyAddedPlugins2));
// check properties are there
Assert.assertEquals(pluginConfig2.getProperties(), pluginArtifactDetail.getMeta().getProperties());
} finally {
artifactRepository.clear(NamespaceId.SYSTEM);
}
}
use of co.cask.cdap.api.plugin.PluginClass 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());
Files.copy(Locations.newInputSupplier(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());
Files.copy(Locations.newInputSupplier(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