use of co.cask.cdap.api.plugin.Plugin 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.Plugin in project cdap by caskdata.
the class PluginClassLoaders method createFilteredPluginsClassLoader.
/**
* Returns a {@link ClassLoader} that only allows loading of plugin classes and plugin exported classes.
* It should only be used in context when a single ClassLoader is needed to load all different kinds of user classes
* (e.g. in MapReduce/Spark).
*/
public static ClassLoader createFilteredPluginsClassLoader(Map<String, Plugin> plugins, @Nullable PluginInstantiator pluginInstantiator) {
if (plugins.isEmpty() || pluginInstantiator == null) {
return new CombineClassLoader(null);
}
try {
// Gather all explicitly used plugin class names. It is needed for external plugin case.
Multimap<Plugin, String> artifactPluginClasses = getArtifactPluginClasses(plugins);
List<ClassLoader> pluginClassLoaders = new ArrayList<>();
for (Plugin plugin : plugins.values()) {
ClassLoader pluginClassLoader = pluginInstantiator.getPluginClassLoader(plugin);
if (pluginClassLoader instanceof PluginClassLoader) {
// A ClassLoader to allow loading of all plugin classes used by the program.
Collection<String> pluginClasses = artifactPluginClasses.get(plugin);
if (!pluginClasses.isEmpty()) {
pluginClassLoaders.add(createClassFilteredClassLoader(pluginClasses, pluginClassLoader));
}
// A ClassLoader to allow all export package classes to be loadable.
pluginClassLoaders.add(((PluginClassLoader) pluginClassLoader).getExportPackagesClassLoader());
}
}
return new CombineClassLoader(null, pluginClassLoaders);
} catch (IOException e) {
throw Throwables.propagate(e);
}
}
use of co.cask.cdap.api.plugin.Plugin 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.Plugin 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.Plugin 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
}
}
}
}
}
Aggregations