Search in sources :

Example 1 with PluginInstantiator

use of io.cdap.cdap.internal.app.runtime.plugin.PluginInstantiator in project cdap by caskdata.

the class DefaultAppConfigurer method addSpark.

@Override
public void addSpark(Spark spark) {
    Preconditions.checkArgument(spark != null, "Spark cannot be null.");
    DefaultSparkConfigurer configurer = null;
    // It is a bit hacky here to look for the DefaultExtendedSparkConfigurer implementation through the
    // SparkRunnerClassloader directly (CDAP-11797)
    ClassLoader sparkRunnerClassLoader = ClassLoaders.findByName(spark.getClass().getClassLoader(), "io.cdap.cdap.app.runtime.spark.classloader.SparkRunnerClassLoader");
    if (sparkRunnerClassLoader != null) {
        try {
            configurer = (DefaultSparkConfigurer) sparkRunnerClassLoader.loadClass("io.cdap.cdap.app.deploy.spark.DefaultExtendedSparkConfigurer").getConstructor(Spark.class, Id.Namespace.class, Id.Artifact.class, PluginFinder.class, PluginInstantiator.class, AppDeploymentRuntimeInfo.class, FeatureFlagsProvider.class).newInstance(spark, deployNamespace, artifactId, pluginFinder, pluginInstantiator, runtimeInfo, getFeatureFlagsProvider());
        } catch (Exception e) {
            // Ignore it and the configurer will be defaulted to DefaultSparkConfigurer
            LOG.trace("No DefaultExtendedSparkConfigurer found. Fallback to DefaultSparkConfigurer.", e);
        }
    }
    if (configurer == null) {
        configurer = new DefaultSparkConfigurer(spark, deployNamespace, artifactId, pluginFinder, pluginInstantiator, runtimeInfo, getFeatureFlagsProvider());
    }
    spark.configure(configurer);
    addDatasetsAndPlugins(configurer);
    SparkSpecification spec = configurer.createSpecification();
    sparks.put(spec.getName(), spec);
}
Also used : SparkSpecification(io.cdap.cdap.api.spark.SparkSpecification) DefaultSparkConfigurer(io.cdap.cdap.internal.app.spark.DefaultSparkConfigurer) PluginFinder(io.cdap.cdap.internal.app.runtime.artifact.PluginFinder) PluginInstantiator(io.cdap.cdap.internal.app.runtime.plugin.PluginInstantiator) Spark(io.cdap.cdap.api.spark.Spark) FeatureFlagsProvider(io.cdap.cdap.api.feature.FeatureFlagsProvider) AppDeploymentRuntimeInfo(io.cdap.cdap.internal.app.deploy.pipeline.AppDeploymentRuntimeInfo)

Example 2 with PluginInstantiator

use of io.cdap.cdap.internal.app.runtime.plugin.PluginInstantiator in project cdap by caskdata.

the class InMemoryConfigurator method createResponse.

private <T extends Config> ConfigResponse createResponse(Application<T> app, ClassLoader artifactClassLoader) throws Exception {
    // This Gson cannot be static since it is used to deserialize user class.
    // Gson will keep a static map to class, hence will leak the classloader
    Gson gson = new GsonBuilder().registerTypeAdapterFactory(new CaseInsensitiveEnumTypeAdapterFactory()).create();
    // Now, we call configure, which returns application specification.
    DefaultAppConfigurer configurer;
    File tempDir = DirUtils.createTempDir(baseUnpackDir);
    try (PluginInstantiator pluginInstantiator = new PluginInstantiator(cConf, app.getClass().getClassLoader(), tempDir)) {
        RuntimeConfigurer runtimeConfigurer = runtimeInfo != null ? new DefaultAppRuntimeConfigurer(appNamespace.getId(), remoteClientFactory, runtimeInfo.getUserArguments(), runtimeInfo.getExistingAppSpec()) : null;
        configurer = new DefaultAppConfigurer(appNamespace, artifactId, app, configString, pluginFinder, pluginInstantiator, runtimeConfigurer, runtimeInfo, featureFlagsProvider);
        T appConfig;
        Type configType = Artifacts.getConfigType(app.getClass());
        if (configString.isEmpty()) {
            // noinspection unchecked
            appConfig = ((Class<T>) configType).newInstance();
        } else {
            try {
                appConfig = gson.fromJson(configString, configType);
            } catch (JsonSyntaxException e) {
                throw new IllegalArgumentException("Invalid JSON configuration was provided. Please check the syntax.", e);
            }
        }
        try {
            ClassLoader oldClassLoader = ClassLoaders.setContextClassLoader(new CombineClassLoader(null, app.getClass().getClassLoader(), getClass().getClassLoader()));
            try {
                app.configure(configurer, new DefaultApplicationContext<>(appConfig));
            } finally {
                ClassLoaders.setContextClassLoader(oldClassLoader);
            }
        } catch (Throwable t) {
            Throwable rootCause = Throwables.getRootCause(t);
            if (rootCause instanceof ClassNotFoundException) {
                // Heuristic to provide better error message
                String missingClass = rootCause.getMessage();
                // If the missing class has "spark" in the name, try to see if Spark is available
                if (missingClass.startsWith("org.apache.spark.") || missingClass.startsWith("io.cdap.cdap.api.spark.")) {
                    // Try to load the SparkContext class, which should be available if Spark is available in the platform
                    try {
                        artifactClassLoader.loadClass("org.apache.spark.SparkContext");
                    } catch (ClassNotFoundException e) {
                        // Spark is not available, it is most likely caused by missing Spark in the platform
                        throw new IllegalStateException("Missing Spark related class " + missingClass + ". It may be caused by unavailability of Spark. " + "Please verify environment variable " + Constants.SPARK_HOME + " is set correctly", t);
                    }
                    // Spark is available, can be caused by incompatible Spark version
                    throw new InvalidArtifactException("Missing Spark related class " + missingClass + ". Configured to use Spark located at " + System.getenv(Constants.SPARK_HOME) + ", which may be incompatible with the one required by the application", t);
                }
                // then the missing class is most likely due to some missing library in the artifact jar
                throw new InvalidArtifactException("Missing class " + missingClass + ". It may be caused by missing dependency jar(s) in the artifact jar.", t);
            }
            throw t;
        }
    } finally {
        try {
            DirUtils.deleteDirectoryContents(tempDir);
        } catch (IOException e) {
            LOG.warn("Exception raised when deleting directory {}", tempDir, e);
        }
    }
    ApplicationSpecification specification = configurer.createSpecification(applicationName, applicationVersion);
    AppSpecInfo appSpecInfo = new AppSpecInfo(specification, configurer.getSystemTables(), configurer.getMetadata());
    return new DefaultConfigResponse(0, appSpecInfo);
}
Also used : ApplicationSpecification(io.cdap.cdap.api.app.ApplicationSpecification) RuntimeConfigurer(io.cdap.cdap.api.app.RuntimeConfigurer) DefaultAppRuntimeConfigurer(io.cdap.cdap.app.DefaultAppRuntimeConfigurer) AppSpecInfo(io.cdap.cdap.internal.app.deploy.pipeline.AppSpecInfo) GsonBuilder(com.google.gson.GsonBuilder) DefaultAppConfigurer(io.cdap.cdap.app.DefaultAppConfigurer) Gson(com.google.gson.Gson) IOException(java.io.IOException) CombineClassLoader(io.cdap.cdap.common.lang.CombineClassLoader) Type(java.lang.reflect.Type) CaseInsensitiveEnumTypeAdapterFactory(io.cdap.cdap.common.io.CaseInsensitiveEnumTypeAdapterFactory) JsonSyntaxException(com.google.gson.JsonSyntaxException) DefaultAppRuntimeConfigurer(io.cdap.cdap.app.DefaultAppRuntimeConfigurer) CloseableClassLoader(io.cdap.cdap.api.artifact.CloseableClassLoader) CombineClassLoader(io.cdap.cdap.common.lang.CombineClassLoader) PluginInstantiator(io.cdap.cdap.internal.app.runtime.plugin.PluginInstantiator) File(java.io.File) InvalidArtifactException(io.cdap.cdap.common.InvalidArtifactException)

Example 3 with PluginInstantiator

use of io.cdap.cdap.internal.app.runtime.plugin.PluginInstantiator in project cdap by caskdata.

the class ServiceProgramRunner method run.

@Override
public ProgramController run(Program program, ProgramOptions options) {
    int instanceId = Integer.parseInt(options.getArguments().getOption(ProgramOptionConstants.INSTANCE_ID, "-1"));
    Preconditions.checkArgument(instanceId >= 0, "Missing instance Id");
    int instanceCount = Integer.parseInt(options.getArguments().getOption(ProgramOptionConstants.INSTANCES, "0"));
    Preconditions.checkArgument(instanceCount > 0, "Invalid or missing instance count");
    RunId runId = ProgramRunners.getRunId(options);
    ApplicationSpecification appSpec = program.getApplicationSpecification();
    Preconditions.checkNotNull(appSpec, "Missing application specification.");
    ProgramType programType = program.getType();
    Preconditions.checkNotNull(programType, "Missing processor type.");
    Preconditions.checkArgument(programType == ProgramType.SERVICE, "Only Service process type is supported.");
    ServiceSpecification spec = appSpec.getServices().get(program.getName());
    String host = options.getArguments().getOption(ProgramOptionConstants.HOST);
    Preconditions.checkArgument(host != null, "No hostname is provided");
    // Setup dataset framework context, if required
    if (datasetFramework instanceof ProgramContextAware) {
        ProgramId programId = program.getId();
        ((ProgramContextAware) datasetFramework).setContext(new BasicProgramContext(programId.run(runId)));
    }
    final PluginInstantiator pluginInstantiator = createPluginInstantiator(options, program.getClassLoader());
    try {
        RetryStrategy retryStrategy = SystemArguments.getRetryStrategy(options.getUserArguments().asMap(), program.getType(), cConf);
        ArtifactManager artifactManager = artifactManagerFactory.create(program.getId().getNamespaceId(), retryStrategy);
        ServiceHttpServer component = new ServiceHttpServer(host, program, options, cConf, spec, instanceId, instanceCount, serviceAnnouncer, metricsCollectionService, datasetFramework, txClient, discoveryServiceClient, pluginInstantiator, secureStore, secureStoreManager, messagingService, artifactManager, metadataReader, metadataPublisher, namespaceQueryAdmin, pluginFinder, fieldLineageWriter, transactionRunner, preferencesFetcher, remoteClientFactory, contextAccessEnforcer);
        // Add a service listener to make sure the plugin instantiator is closed when the http server is finished.
        component.addListener(createRuntimeServiceListener(Collections.singleton(pluginInstantiator)), Threads.SAME_THREAD_EXECUTOR);
        ProgramController controller = new ServiceProgramControllerAdapter(component, program.getId().run(runId));
        component.start();
        return controller;
    } catch (Throwable t) {
        Closeables.closeQuietly(pluginInstantiator);
        throw t;
    }
}
Also used : ApplicationSpecification(io.cdap.cdap.api.app.ApplicationSpecification) ServiceSpecification(io.cdap.cdap.api.service.ServiceSpecification) ProgramController(io.cdap.cdap.app.runtime.ProgramController) ProgramId(io.cdap.cdap.proto.id.ProgramId) BasicProgramContext(io.cdap.cdap.internal.app.runtime.BasicProgramContext) ServiceHttpServer(io.cdap.cdap.internal.app.services.ServiceHttpServer) ArtifactManager(io.cdap.cdap.api.artifact.ArtifactManager) PluginInstantiator(io.cdap.cdap.internal.app.runtime.plugin.PluginInstantiator) ProgramType(io.cdap.cdap.proto.ProgramType) ProgramRunId(io.cdap.cdap.proto.id.ProgramRunId) RunId(org.apache.twill.api.RunId) ProgramContextAware(io.cdap.cdap.data.ProgramContextAware) RetryStrategy(io.cdap.cdap.common.service.RetryStrategy)

Example 4 with PluginInstantiator

use of io.cdap.cdap.internal.app.runtime.plugin.PluginInstantiator in project cdap by caskdata.

the class DefaultArtifactInspector method inspectArtifact.

/**
 * Inspect the given artifact to determine the classes contained in the artifact.
 *
 * @param artifactId the id of the artifact to inspect
 * @param artifactFile the artifact file
 * @param parentDescriptor {@link ArtifactDescriptor} of parent and grandparent (if any) artifacts.
 * @param additionalPlugins Additional plugin classes
 * @return metadata about the classes contained in the artifact
 * @throws IOException              if there was an exception opening the jar file
 * @throws InvalidArtifactException if the artifact is invalid. For example, if the application main class is not
 *                                  actually an Application.
 */
@Override
public ArtifactClassesWithMetadata inspectArtifact(Id.Artifact artifactId, File artifactFile, List<ArtifactDescriptor> parentDescriptor, Set<PluginClass> additionalPlugins) throws IOException, InvalidArtifactException {
    Path tmpDir = Paths.get(cConf.get(Constants.CFG_LOCAL_DATA_DIR), cConf.get(Constants.AppFabric.TEMP_DIR)).toAbsolutePath();
    Files.createDirectories(tmpDir);
    Location artifactLocation = Locations.toLocation(artifactFile);
    EntityImpersonator entityImpersonator = new EntityImpersonator(artifactId.toEntityId(), impersonator);
    Path stageDir = Files.createTempDirectory(tmpDir, artifactFile.getName());
    try (ClassLoaderFolder clFolder = BundleJarUtil.prepareClassLoaderFolder(artifactLocation, () -> Files.createTempDirectory(stageDir, "unpacked-").toFile());
        CloseableClassLoader parentClassLoader = createParentClassLoader(parentDescriptor, entityImpersonator);
        CloseableClassLoader artifactClassLoader = artifactClassLoaderFactory.createClassLoader(clFolder.getDir());
        PluginInstantiator pluginInstantiator = new PluginInstantiator(cConf, parentClassLoader == null ? artifactClassLoader : parentClassLoader, Files.createTempDirectory(stageDir, "plugins-").toFile(), false)) {
        pluginInstantiator.addArtifact(artifactLocation, artifactId.toArtifactId());
        ArtifactClasses.Builder builder = inspectApplications(artifactId, ArtifactClasses.builder(), artifactLocation, artifactClassLoader);
        List<MetadataMutation> mutations = new ArrayList<>();
        inspectPlugins(builder, artifactFile, artifactId.toEntityId(), pluginInstantiator, additionalPlugins, mutations);
        return new ArtifactClassesWithMetadata(builder.build(), mutations);
    } catch (EOFException | ZipException e) {
        throw new InvalidArtifactException("Artifact " + artifactId + " is not a valid zip file.", e);
    } finally {
        try {
            DirUtils.deleteDirectoryContents(stageDir.toFile());
        } catch (IOException e) {
            LOG.warn("Exception raised while deleting directory {}", stageDir, e);
        }
    }
}
Also used : Path(java.nio.file.Path) EntityImpersonator(io.cdap.cdap.security.impersonation.EntityImpersonator) ArrayList(java.util.ArrayList) ZipException(java.util.zip.ZipException) CloseableClassLoader(io.cdap.cdap.api.artifact.CloseableClassLoader) IOException(java.io.IOException) ClassLoaderFolder(io.cdap.cdap.common.lang.jar.ClassLoaderFolder) MetadataMutation(io.cdap.cdap.spi.metadata.MetadataMutation) ArtifactClasses(io.cdap.cdap.api.artifact.ArtifactClasses) EOFException(java.io.EOFException) PluginInstantiator(io.cdap.cdap.internal.app.runtime.plugin.PluginInstantiator) InvalidArtifactException(io.cdap.cdap.common.InvalidArtifactException) Location(org.apache.twill.filesystem.Location)

Example 5 with PluginInstantiator

use of io.cdap.cdap.internal.app.runtime.plugin.PluginInstantiator 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<>(), 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) InvalidPluginConfigException(io.cdap.cdap.api.plugin.InvalidPluginConfigException) PluginInstantiator(io.cdap.cdap.internal.app.runtime.plugin.PluginInstantiator) PluginClass(io.cdap.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) TestPlugin(io.cdap.cdap.internal.app.plugins.test.TestPlugin) NestedConfigPlugin(io.cdap.cdap.internal.app.runtime.artifact.plugin.nested.NestedConfigPlugin) Plugin(io.cdap.cdap.api.plugin.Plugin) Test(org.junit.Test)

Aggregations

PluginInstantiator (io.cdap.cdap.internal.app.runtime.plugin.PluginInstantiator)16 File (java.io.File)7 ApplicationSpecification (io.cdap.cdap.api.app.ApplicationSpecification)6 ProgramContextAware (io.cdap.cdap.data.ProgramContextAware)6 BasicProgramContext (io.cdap.cdap.internal.app.runtime.BasicProgramContext)6 IOException (java.io.IOException)6 ProgramController (io.cdap.cdap.app.runtime.ProgramController)5 ProgramType (io.cdap.cdap.proto.ProgramType)5 ProgramId (io.cdap.cdap.proto.id.ProgramId)5 RunId (org.apache.twill.api.RunId)5 ImmutableMap (com.google.common.collect.ImmutableMap)4 PluginClass (io.cdap.cdap.api.plugin.PluginClass)4 CConfiguration (io.cdap.cdap.common.conf.CConfiguration)4 NestedConfigPlugin (io.cdap.cdap.internal.app.runtime.artifact.plugin.nested.NestedConfigPlugin)4 ImmutableSet (com.google.common.collect.ImmutableSet)3 Service (com.google.common.util.concurrent.Service)3 Gson (com.google.gson.Gson)3 MetricsCollectionService (io.cdap.cdap.api.metrics.MetricsCollectionService)3 Plugin (io.cdap.cdap.api.plugin.Plugin)3 TestPlugin (io.cdap.cdap.internal.app.plugins.test.TestPlugin)3