Search in sources :

Example 11 with Plugin

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;
}
Also used : LinkedHashSet(java.util.LinkedHashSet) ArtifactId(co.cask.cdap.proto.id.ArtifactId) PluginNotExistsException(co.cask.cdap.internal.app.runtime.plugin.PluginNotExistsException) ArtifactDescriptor(co.cask.cdap.internal.app.runtime.artifact.ArtifactDescriptor) PluginClassLoader(co.cask.cdap.internal.app.runtime.plugin.PluginClassLoader) PluginClass(co.cask.cdap.api.plugin.PluginClass) PluginClass(co.cask.cdap.api.plugin.PluginClass) HashMap(java.util.HashMap) Map(java.util.Map) PluginClassLoader(co.cask.cdap.internal.app.runtime.plugin.PluginClassLoader) Plugin(co.cask.cdap.api.plugin.Plugin)

Example 12 with Plugin

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);
    }
}
Also used : CombineClassLoader(co.cask.cdap.common.lang.CombineClassLoader) ArrayList(java.util.ArrayList) FilterClassLoader(co.cask.cdap.common.lang.FilterClassLoader) CombineClassLoader(co.cask.cdap.common.lang.CombineClassLoader) IOException(java.io.IOException) Plugin(co.cask.cdap.api.plugin.Plugin)

Example 13 with Plugin

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);
}
Also used : Path(javax.ws.rs.Path) ArtifactId(co.cask.cdap.api.artifact.ArtifactId) EndpointPluginContext(co.cask.cdap.api.plugin.EndpointPluginContext) DefaultEndpointPluginContext(co.cask.cdap.internal.app.runtime.DefaultEndpointPluginContext) ArtifactNotFoundException(co.cask.cdap.common.ArtifactNotFoundException) NotFoundException(co.cask.cdap.common.NotFoundException) Method(java.lang.reflect.Method) CloseableClassLoader(co.cask.cdap.api.artifact.CloseableClassLoader) PluginClass(co.cask.cdap.api.plugin.PluginClass) Plugin(co.cask.cdap.api.plugin.Plugin)

Example 14 with Plugin

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());
            }
        }
    }
}
Also used : ImmutableSet(com.google.common.collect.ImmutableSet) Set(java.util.Set) HashSet(java.util.HashSet) ArtifactId(co.cask.cdap.api.artifact.ArtifactId) PluginInstantiator(co.cask.cdap.internal.app.runtime.plugin.PluginInstantiator) PluginClass(co.cask.cdap.api.plugin.PluginClass) File(java.io.File) Map(java.util.Map) ImmutableMap(com.google.common.collect.ImmutableMap) SortedMap(java.util.SortedMap) HashMap(java.util.HashMap) Plugin(co.cask.cdap.api.plugin.Plugin) TestPlugin(co.cask.cdap.internal.app.plugins.test.TestPlugin) Test(org.junit.Test)

Example 15 with Plugin

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
                }
            }
        }
    }
}
Also used : ImmutableSet(com.google.common.collect.ImmutableSet) Set(java.util.Set) HashSet(java.util.HashSet) ArtifactId(co.cask.cdap.api.artifact.ArtifactId) InvalidPluginConfigException(co.cask.cdap.internal.app.runtime.plugin.InvalidPluginConfigException) PluginInstantiator(co.cask.cdap.internal.app.runtime.plugin.PluginInstantiator) PluginClass(co.cask.cdap.api.plugin.PluginClass) File(java.io.File) Map(java.util.Map) ImmutableMap(com.google.common.collect.ImmutableMap) SortedMap(java.util.SortedMap) HashMap(java.util.HashMap) Plugin(co.cask.cdap.api.plugin.Plugin) TestPlugin(co.cask.cdap.internal.app.plugins.test.TestPlugin) Test(org.junit.Test)

Aggregations

Plugin (co.cask.cdap.api.plugin.Plugin)16 ArtifactId (co.cask.cdap.api.artifact.ArtifactId)7 Map (java.util.Map)7 PluginClass (co.cask.cdap.api.plugin.PluginClass)6 ImmutableMap (com.google.common.collect.ImmutableMap)5 IOException (java.io.IOException)5 HashMap (java.util.HashMap)5 ArtifactNotFoundException (co.cask.cdap.common.ArtifactNotFoundException)4 File (java.io.File)4 HashSet (java.util.HashSet)4 TestPlugin (co.cask.cdap.internal.app.plugins.test.TestPlugin)3 PluginInstantiator (co.cask.cdap.internal.app.runtime.plugin.PluginInstantiator)3 PluginNotExistsException (co.cask.cdap.internal.app.runtime.plugin.PluginNotExistsException)3 ImmutableSet (com.google.common.collect.ImmutableSet)3 ArrayList (java.util.ArrayList)3 Set (java.util.Set)3 SortedMap (java.util.SortedMap)3 Test (org.junit.Test)3 StreamSpecification (co.cask.cdap.api.data.stream.StreamSpecification)2 PluginPropertyField (co.cask.cdap.api.plugin.PluginPropertyField)2