/* * Copyright © 2015 Cask Data, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package co.cask.cdap.internal.app.runtime.artifact; import co.cask.cdap.app.runtime.ProgramRunner; import co.cask.cdap.app.runtime.ProgramRunnerFactory; import co.cask.cdap.common.conf.CConfiguration; import co.cask.cdap.common.conf.Constants; import co.cask.cdap.common.lang.FilterClassLoader; import co.cask.cdap.common.lang.ProgramClassLoader; import co.cask.cdap.common.lang.ProgramClassLoaderProvider; import co.cask.cdap.common.lang.jar.BundleJarUtil; import co.cask.cdap.common.utils.DirUtils; import co.cask.cdap.proto.ProgramType; import com.google.common.io.Closeables; import org.apache.twill.filesystem.Location; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Closeable; import java.io.File; import java.io.IOException; /** * Given an artifact, creates a {@link CloseableClassLoader} from it. Takes care of unpacking the artifact and * cleaning up the directory when the classloader is closed. */ final class ArtifactClassLoaderFactory { private static final Logger LOG = LoggerFactory.getLogger(ArtifactClassLoaderFactory.class); private final CConfiguration cConf; private final ProgramRunnerFactory programRunnerFactory; private final File tmpDir; ArtifactClassLoaderFactory(CConfiguration cConf, ProgramRunnerFactory programRunnerFactory) { this.cConf = cConf; this.programRunnerFactory = programRunnerFactory; this.tmpDir = new File(cConf.get(Constants.CFG_LOCAL_DATA_DIR), cConf.get(Constants.AppFabric.TEMP_DIR)).getAbsoluteFile(); } /** * Create a classloader that uses the artifact at the specified location to load classes, with access to * packages that all program type has access to. The classloader created is only for artifact inspection purpose * and shouldn't be used for program execution as it doesn't have the proper class filtering for the specific * program type for the program being executed. * * @param artifactLocation the location of the artifact to create the classloader from * @return a closeable classloader based off the specified artifact; on closing the returned {@link ClassLoader}, * all temporary resources created for the classloader will be removed * @throws IOException if there was an error copying or unpacking the artifact */ CloseableClassLoader createClassLoader(Location artifactLocation) throws IOException { final File unpackDir = BundleJarUtil.unJar(artifactLocation, DirUtils.createTempDir(tmpDir)); ProgramRunner programRunner = null; ProgramClassLoader programClassLoader = null; try { // Try to create a ProgramClassLoader from the Spark runtime system if it is available. // It is needed because we don't know what program types that an artifact might have. // TODO: CDAP-5613. We shouldn't always expose the Spark classes. programRunner = programRunnerFactory.create(ProgramType.SPARK); if (programRunner instanceof ProgramClassLoaderProvider) { programClassLoader = ((ProgramClassLoaderProvider) programRunner).createProgramClassLoader(cConf, unpackDir); } } catch (Exception e) { // If Spark is not supported, exception is expected. We'll use the default filter. LOG.trace("Spark is not supported. Not using ProgramClassLoader from Spark", e); } if (programClassLoader == null) { programClassLoader = new ProgramClassLoader(cConf, unpackDir, FilterClassLoader.create(getClass().getClassLoader())); } final ProgramClassLoader finalProgramClassLoader = programClassLoader; final ProgramRunner finalProgramRunner = programRunner; return new CloseableClassLoader(programClassLoader, new Closeable() { @Override public void close() { try { Closeables.closeQuietly(finalProgramClassLoader); if (finalProgramRunner instanceof Closeable) { Closeables.closeQuietly((Closeable) finalProgramRunner); } DirUtils.deleteDirectoryContents(unpackDir); } catch (IOException e) { LOG.warn("Failed to delete directory {}", unpackDir, e); } } }); } }