Search in sources :

Example 1 with CombineClassLoader

use of co.cask.cdap.common.lang.CombineClassLoader in project cdap by caskdata.

the class AbstractHttpHandlerDelegator method wrapResponder.

/**
   * Returns a new instance of {@link DelayedHttpServiceResponder} that wraps around the given {@link HttpResponder}
   * object. This method is called from handler class generated by {@link HttpHandlerGenerator}.
   */
@SuppressWarnings("unused")
protected final DelayedHttpServiceResponder wrapResponder(HttpResponder responder) {
    MetricsContext collector = this.metricsContext;
    HttpServiceContext serviceContext = context.getServiceContext();
    Preconditions.checkState(serviceContext instanceof TransactionalHttpServiceContext, "This instance of HttpServiceContext does not support transactions.");
    if (serviceContext.getSpecification() != null) {
        collector = metricsContext.childContext(Constants.Metrics.Tag.HANDLER, serviceContext.getSpecification().getName());
    }
    return new DelayedHttpServiceResponder(responder, new BodyProducerFactory() {

        @Override
        public BodyProducer create(HttpContentProducer contentProducer, TransactionalHttpServiceContext serviceContext) {
            final ClassLoader programContextClassLoader = new CombineClassLoader(null, ImmutableList.of(contentProducer.getClass().getClassLoader(), getClass().getClassLoader()));
            // Capture the context since we need to keep it until the end of the content producing.
            // We don't need to worry about double capturing of the context when HttpContentConsumer is used.
            // This is because when HttpContentConsumer is used, the responder constructed here will get closed and this
            // BodyProducerFactory won't be used.
            final Cancellable contextReleaser = context.capture();
            return new BodyProducerAdapter(contentProducer, serviceContext, programContextClassLoader, contextReleaser);
        }
    }, (TransactionalHttpServiceContext) serviceContext, collector);
}
Also used : BodyProducer(co.cask.http.BodyProducer) CombineClassLoader(co.cask.cdap.common.lang.CombineClassLoader) HttpServiceContext(co.cask.cdap.api.service.http.HttpServiceContext) Cancellable(org.apache.twill.common.Cancellable) MetricsContext(co.cask.cdap.api.metrics.MetricsContext) HttpContentProducer(co.cask.cdap.api.service.http.HttpContentProducer) CombineClassLoader(co.cask.cdap.common.lang.CombineClassLoader)

Example 2 with CombineClassLoader

use of co.cask.cdap.common.lang.CombineClassLoader in project cdap by caskdata.

the class AbstractHttpHandlerDelegator method wrapContentConsumer.

/**
   * Returns a new instance of {@link BodyConsumer} that wraps around the given {@link HttpContentConsumer}
   * and {@link DelayedHttpServiceResponder}.
   *
   * IMPORTANT: This method will also capture the context associated with the current thread, hence after
   * this method is called, no other methods on this class should be called from the current thread.
   *
   * This method is called from handler class generated by {@link HttpHandlerGenerator}.
   */
@SuppressWarnings("unused")
protected final BodyConsumer wrapContentConsumer(HttpContentConsumer consumer, DelayedHttpServiceResponder responder) {
    Preconditions.checkState(!responder.hasBufferedResponse(), "HttpContentConsumer may not be used after a response has already been sent.");
    // Close the provided responder since a new one will be created for the BodyConsumerAdapter to use.
    responder.close();
    final HttpServiceContext serviceContext = context.getServiceContext();
    Preconditions.checkState(serviceContext instanceof TransactionalHttpServiceContext, "This instance of HttpServiceContext does not support transactions.");
    final Cancellable contextReleaser = context.capture();
    final ClassLoader programContextClassLoader = new CombineClassLoader(null, ImmutableList.of(consumer.getClass().getClassLoader(), getClass().getClassLoader()));
    return new BodyConsumerAdapter(new DelayedHttpServiceResponder(responder, new BodyProducerFactory() {

        @Override
        public BodyProducer create(HttpContentProducer contentProducer, TransactionalHttpServiceContext serviceContext) {
            // Transfer the captured context from the content consumer to the content producer
            return new BodyProducerAdapter(contentProducer, serviceContext, programContextClassLoader, contextReleaser);
        }
    }), consumer, serviceContext, programContextClassLoader, contextReleaser);
}
Also used : CombineClassLoader(co.cask.cdap.common.lang.CombineClassLoader) HttpServiceContext(co.cask.cdap.api.service.http.HttpServiceContext) Cancellable(org.apache.twill.common.Cancellable) HttpContentProducer(co.cask.cdap.api.service.http.HttpContentProducer) CombineClassLoader(co.cask.cdap.common.lang.CombineClassLoader)

Example 3 with CombineClassLoader

use of co.cask.cdap.common.lang.CombineClassLoader in project cdap by caskdata.

the class MainClassLoader method createFromContext.

/**
 * @param filter A {@link FilterClassLoader.Filter} for filtering out classes from the
 * @param extraClasspath extra list of {@link URL} to be added to the end of the classpath for the
 *                       {@link MainClassLoader} to be created
 * @return a new instance from the current context classloader or the system classloader. The returned
 * {@link MainClassLoader} will be the defining classloader for classes in the context classloader
 * that the filter rejected. For classes that pass the filter, the defining classloader will be the original
 * context classloader.
 * It will return {@code null} if it is not able to create a new instance due to lack of classpath information.
 */
@Nullable
public static MainClassLoader createFromContext(FilterClassLoader.Filter filter, URL... extraClasspath) {
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    if (classLoader == null) {
        classLoader = ClassLoader.getSystemClassLoader();
    }
    List<URL> classpath = new ArrayList<>();
    if (classLoader instanceof URLClassLoader) {
        classpath.addAll(Arrays.asList(((URLClassLoader) classLoader).getURLs()));
    } else if (classLoader == ClassLoader.getSystemClassLoader()) {
        addClassPath(classpath);
    } else {
        // No able to create a new MainClassLoader
        return null;
    }
    classpath.addAll(Arrays.asList(extraClasspath));
    ClassLoader filtered = new FilterClassLoader(classLoader, filter);
    ClassLoader parent = new CombineClassLoader(classLoader.getParent(), filtered);
    return new MainClassLoader(classpath.toArray(new URL[classpath.size()]), parent);
}
Also used : CombineClassLoader(co.cask.cdap.common.lang.CombineClassLoader) FilterClassLoader(co.cask.cdap.common.lang.FilterClassLoader) URLClassLoader(java.net.URLClassLoader) ArrayList(java.util.ArrayList) InterceptableClassLoader(co.cask.cdap.common.lang.InterceptableClassLoader) URLClassLoader(java.net.URLClassLoader) FilterClassLoader(co.cask.cdap.common.lang.FilterClassLoader) CombineClassLoader(co.cask.cdap.common.lang.CombineClassLoader) URL(java.net.URL) Nullable(javax.annotation.Nullable)

Example 4 with CombineClassLoader

use of co.cask.cdap.common.lang.CombineClassLoader in project cdap by caskdata.

the class InMemoryConfigurator method getSpecJson.

private <T extends Config> String getSpecJson(Application<T> app) 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)) {
        configurer = new DefaultAppConfigurer(appNamespace, artifactId, app, configString, artifactRepository, pluginInstantiator);
        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("co.cask.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);
    // TODO: The SchemaGenerator should be injected
    return ApplicationSpecificationAdapter.create(new ReflectionSchemaGenerator()).toJson(specification);
}
Also used : ApplicationSpecification(co.cask.cdap.api.app.ApplicationSpecification) GsonBuilder(com.google.gson.GsonBuilder) DefaultAppConfigurer(co.cask.cdap.app.DefaultAppConfigurer) Gson(com.google.gson.Gson) IOException(java.io.IOException) ReflectionSchemaGenerator(co.cask.cdap.internal.io.ReflectionSchemaGenerator) CombineClassLoader(co.cask.cdap.common.lang.CombineClassLoader) Type(java.lang.reflect.Type) CaseInsensitiveEnumTypeAdapterFactory(co.cask.cdap.common.io.CaseInsensitiveEnumTypeAdapterFactory) JsonSyntaxException(com.google.gson.JsonSyntaxException) CombineClassLoader(co.cask.cdap.common.lang.CombineClassLoader) PluginInstantiator(co.cask.cdap.internal.app.runtime.plugin.PluginInstantiator) File(java.io.File) InvalidArtifactException(co.cask.cdap.common.InvalidArtifactException)

Example 5 with CombineClassLoader

use of co.cask.cdap.common.lang.CombineClassLoader in project cdap by caskdata.

the class DistributedProgramRunner method run.

@Override
public final ProgramController run(final Program program, ProgramOptions oldOptions) {
    validateOptions(program, oldOptions);
    final CConfiguration cConf = createContainerCConf(this.cConf);
    final Configuration hConf = createContainerHConf(this.hConf);
    final File tempDir = DirUtils.createTempDir(new File(cConf.get(Constants.CFG_LOCAL_DATA_DIR), cConf.get(Constants.AppFabric.TEMP_DIR)).getAbsoluteFile());
    try {
        final LaunchConfig launchConfig = new LaunchConfig();
        setupLaunchConfig(launchConfig, program, oldOptions, cConf, hConf, tempDir);
        // Add extra localize resources needed by the program runner
        final Map<String, LocalizeResource> localizeResources = new HashMap<>(launchConfig.getExtraResources());
        final ProgramOptions options = addArtifactPluginFiles(oldOptions, localizeResources, DirUtils.createTempDir(tempDir));
        final List<String> additionalClassPaths = new ArrayList<>();
        addContainerJars(cConf, localizeResources, additionalClassPaths);
        prepareHBaseDDLExecutorResources(tempDir, cConf, localizeResources);
        // Save the configuration to files
        final File hConfFile = saveHConf(hConf, new File(tempDir, HADOOP_CONF_FILE_NAME));
        final File cConfFile = saveCConf(cConf, new File(tempDir, CDAP_CONF_FILE_NAME));
        // Localize the program jar
        Location programJarLocation = program.getJarLocation();
        final String programJarName = programJarLocation.getName();
        localizeResources.put(programJarName, new LocalizeResource(program.getJarLocation().toURI(), false));
        // Localize an expanded program jar
        final String expandedProgramJarName = "expanded." + programJarName;
        localizeResources.put(expandedProgramJarName, new LocalizeResource(program.getJarLocation().toURI(), true));
        // Localize the app spec
        localizeResources.put(APP_SPEC_FILE_NAME, new LocalizeResource(saveAppSpec(program, File.createTempFile("appSpec", ".json", tempDir))));
        final URI logbackURI = getLogBackURI(program, tempDir);
        if (logbackURI != null) {
            // Localize the logback xml
            localizeResources.put(LOGBACK_FILE_NAME, new LocalizeResource(logbackURI, false));
        }
        Callable<ProgramController> callable = new Callable<ProgramController>() {

            @Override
            public ProgramController call() throws Exception {
                ProgramTwillApplication twillApplication = new ProgramTwillApplication(program.getId(), launchConfig.getRunnables(), launchConfig.getLaunchOrder(), localizeResources, createEventHandler(cConf, options));
                TwillPreparer twillPreparer = twillRunner.prepare(twillApplication);
                // Add the configuration to container classpath
                twillPreparer.withResources(hConfFile.toURI(), cConfFile.toURI());
                Map<String, String> userArgs = options.getUserArguments().asMap();
                // Setup log level
                twillPreparer.setLogLevels(transformLogLevels(SystemArguments.getLogLevels(userArgs)));
                // Set the configuration for the twill application
                Map<String, String> twillConfigs = new HashMap<>();
                if (DistributedProgramRunner.this instanceof LongRunningDistributedProgramRunner) {
                    twillConfigs.put(Configs.Keys.YARN_ATTEMPT_FAILURES_VALIDITY_INTERVAL, cConf.get(Constants.AppFabric.YARN_ATTEMPT_FAILURES_VALIDITY_INTERVAL));
                }
                // Add the one from the runtime arguments
                twillConfigs.putAll(SystemArguments.getTwillApplicationConfigs(userArgs));
                twillPreparer.withConfiguration(twillConfigs);
                // Setup per runnable configurations
                for (Map.Entry<String, RunnableDefinition> entry : launchConfig.getRunnables().entrySet()) {
                    String runnable = entry.getKey();
                    RunnableDefinition runnableDefinition = entry.getValue();
                    if (runnableDefinition.getMaxRetries() != null) {
                        twillPreparer.withMaxRetries(runnable, runnableDefinition.getMaxRetries());
                    }
                    twillPreparer.setLogLevels(runnable, transformLogLevels(runnableDefinition.getLogLevels()));
                    twillPreparer.withConfiguration(runnable, runnableDefinition.getTwillRunnableConfigs());
                }
                if (options.isDebug()) {
                    twillPreparer.enableDebugging();
                }
                logProgramStart(program, options);
                String serializedOptions = GSON.toJson(options, ProgramOptions.class);
                LOG.info("Starting {} with debugging enabled: {}, programOptions: {}, and logback: {}", program.getId(), options.isDebug(), serializedOptions, logbackURI);
                // Add scheduler queue name if defined
                String schedulerQueueName = options.getArguments().getOption(Constants.AppFabric.APP_SCHEDULER_QUEUE);
                if (schedulerQueueName != null && !schedulerQueueName.isEmpty()) {
                    LOG.info("Setting scheduler queue for app {} as {}", program.getId(), schedulerQueueName);
                    twillPreparer.setSchedulerQueue(schedulerQueueName);
                }
                if (logbackURI != null) {
                    twillPreparer.addJVMOptions("-Dlogback.configurationFile=" + LOGBACK_FILE_NAME);
                }
                String logLevelConf = cConf.get(Constants.COLLECT_APP_CONTAINER_LOG_LEVEL).toUpperCase();
                if ("OFF".equals(logLevelConf)) {
                    twillPreparer.withConfiguration(Collections.singletonMap(Configs.Keys.LOG_COLLECTION_ENABLED, "false"));
                } else {
                    LogEntry.Level logLevel = LogEntry.Level.ERROR;
                    if ("ALL".equals(logLevelConf)) {
                        logLevel = LogEntry.Level.TRACE;
                    } else {
                        try {
                            logLevel = LogEntry.Level.valueOf(logLevelConf.toUpperCase());
                        } catch (Exception e) {
                            LOG.warn("Invalid application container log level {}. Defaulting to ERROR.", logLevelConf);
                        }
                    }
                    twillPreparer.addLogHandler(new LoggerLogHandler(LOG, logLevel));
                }
                // Add secure tokens
                if (User.isHBaseSecurityEnabled(hConf) || UserGroupInformation.isSecurityEnabled()) {
                    twillPreparer.addSecureStore(YarnSecureStore.create(secureStoreRenewer.createCredentials()));
                }
                // Setup the environment for the container logback.xml
                twillPreparer.withEnv(Collections.singletonMap("CDAP_LOG_DIR", ApplicationConstants.LOG_DIR_EXPANSION_VAR));
                // Add dependencies
                Set<Class<?>> extraDependencies = new HashSet<>(launchConfig.getExtraDependencies());
                extraDependencies.add(HBaseTableUtilFactory.getHBaseTableUtilClass(cConf));
                if (SecureStoreUtils.isKMSBacked(cConf) && SecureStoreUtils.isKMSCapable()) {
                    extraDependencies.add(SecureStoreUtils.getKMSSecureStore());
                }
                twillPreparer.withDependencies(extraDependencies);
                // Add the additional classes to the classpath that comes from the container jar setting
                twillPreparer.withClassPaths(additionalClassPaths);
                twillPreparer.withClassPaths(launchConfig.getExtraClasspath());
                twillPreparer.withEnv(launchConfig.getExtraEnv());
                // The Yarn app classpath goes last
                List<String> yarnAppClassPath = Arrays.asList(hConf.getTrimmedStrings(YarnConfiguration.YARN_APPLICATION_CLASSPATH, YarnConfiguration.DEFAULT_YARN_APPLICATION_CLASSPATH));
                twillPreparer.withApplicationClassPaths(yarnAppClassPath).withClassPaths(yarnAppClassPath).withBundlerClassAcceptor(launchConfig.getClassAcceptor()).withApplicationArguments("--" + RunnableOptions.JAR, programJarName, "--" + RunnableOptions.EXPANDED_JAR, expandedProgramJarName, "--" + RunnableOptions.HADOOP_CONF_FILE, HADOOP_CONF_FILE_NAME, "--" + RunnableOptions.CDAP_CONF_FILE, CDAP_CONF_FILE_NAME, "--" + RunnableOptions.APP_SPEC_FILE, APP_SPEC_FILE_NAME, "--" + RunnableOptions.PROGRAM_OPTIONS, serializedOptions, "--" + RunnableOptions.PROGRAM_ID, GSON.toJson(program.getId())).setClassLoader(MainClassLoader.class.getName());
                // Invoke the before launch hook
                beforeLaunch(program, options);
                TwillController twillController;
                // Change the context classloader to the combine classloader of this ProgramRunner and
                // all the classloaders of the dependencies classes so that Twill can trace classes.
                ClassLoader oldClassLoader = ClassLoaders.setContextClassLoader(new CombineClassLoader(DistributedProgramRunner.this.getClass().getClassLoader(), Iterables.transform(extraDependencies, new Function<Class<?>, ClassLoader>() {

                    @Override
                    public ClassLoader apply(Class<?> input) {
                        return input.getClassLoader();
                    }
                })));
                try {
                    twillController = twillPreparer.start(cConf.getLong(Constants.AppFabric.PROGRAM_MAX_START_SECONDS), TimeUnit.SECONDS);
                } finally {
                    ClassLoaders.setContextClassLoader(oldClassLoader);
                }
                return createProgramController(addCleanupListener(twillController, program, tempDir), new ProgramDescriptor(program.getId(), program.getApplicationSpecification()), ProgramRunners.getRunId(options));
            }
        };
        return impersonator.doAs(program.getId(), callable);
    } catch (Exception e) {
        deleteDirectory(tempDir);
        throw Throwables.propagate(e);
    }
}
Also used : CConfiguration(co.cask.cdap.common.conf.CConfiguration) Configuration(org.apache.hadoop.conf.Configuration) YarnConfiguration(org.apache.hadoop.yarn.conf.YarnConfiguration) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) URI(java.net.URI) Callable(java.util.concurrent.Callable) CombineClassLoader(co.cask.cdap.common.lang.CombineClassLoader) MainClassLoader(co.cask.cdap.common.app.MainClassLoader) CombineClassLoader(co.cask.cdap.common.lang.CombineClassLoader) ProgramDescriptor(co.cask.cdap.app.program.ProgramDescriptor) TwillPreparer(org.apache.twill.api.TwillPreparer) LogEntry(org.apache.twill.api.logging.LogEntry) LoggerLogHandler(co.cask.cdap.common.logging.LoggerLogHandler) HashSet(java.util.HashSet) ProgramController(co.cask.cdap.app.runtime.ProgramController) CConfiguration(co.cask.cdap.common.conf.CConfiguration) SimpleProgramOptions(co.cask.cdap.internal.app.runtime.SimpleProgramOptions) ProgramOptions(co.cask.cdap.app.runtime.ProgramOptions) URISyntaxException(java.net.URISyntaxException) IOException(java.io.IOException) TwillController(org.apache.twill.api.TwillController) File(java.io.File) Map(java.util.Map) HashMap(java.util.HashMap) Location(org.apache.twill.filesystem.Location) MainClassLoader(co.cask.cdap.common.app.MainClassLoader)

Aggregations

CombineClassLoader (co.cask.cdap.common.lang.CombineClassLoader)7 IOException (java.io.IOException)3 ArrayList (java.util.ArrayList)3 HttpContentProducer (co.cask.cdap.api.service.http.HttpContentProducer)2 HttpServiceContext (co.cask.cdap.api.service.http.HttpServiceContext)2 FilterClassLoader (co.cask.cdap.common.lang.FilterClassLoader)2 File (java.io.File)2 Cancellable (org.apache.twill.common.Cancellable)2 ApplicationSpecification (co.cask.cdap.api.app.ApplicationSpecification)1 MetricsContext (co.cask.cdap.api.metrics.MetricsContext)1 Plugin (co.cask.cdap.api.plugin.Plugin)1 DefaultAppConfigurer (co.cask.cdap.app.DefaultAppConfigurer)1 ProgramDescriptor (co.cask.cdap.app.program.ProgramDescriptor)1 ProgramController (co.cask.cdap.app.runtime.ProgramController)1 ProgramOptions (co.cask.cdap.app.runtime.ProgramOptions)1 InvalidArtifactException (co.cask.cdap.common.InvalidArtifactException)1 MainClassLoader (co.cask.cdap.common.app.MainClassLoader)1 CConfiguration (co.cask.cdap.common.conf.CConfiguration)1 CaseInsensitiveEnumTypeAdapterFactory (co.cask.cdap.common.io.CaseInsensitiveEnumTypeAdapterFactory)1 DirectoryClassLoader (co.cask.cdap.common.lang.DirectoryClassLoader)1