use of co.cask.cdap.api.artifact.CloseableClassLoader in project cdap by caskdata.
the class ArtifactRepository method addArtifact.
/**
* Inspects and builds plugin and application information for the given artifact, adding an additional set of
* plugin classes to the plugins found through inspection. This method is used when all plugin classes
* cannot be derived by inspecting the artifact but need to be explicitly set. This is true for 3rd party plugins
* like jdbc drivers.
*
* @param artifactId the id of the artifact to inspect and store
* @param artifactFile the artifact to inspect and store
* @param parentArtifacts artifacts the given artifact extends.
* If null, the given artifact does not extend another artifact
* @param additionalPlugins the set of additional plugin classes to add to the plugins found through inspection.
* If null, no additional plugin classes will be added
* @param properties properties for the artifact
* @throws IOException if there was an exception reading from the artifact store
* @throws ArtifactRangeNotFoundException if none of the parent artifacts could be found
* @throws UnauthorizedException if the user is not authorized to add an artifact in the specified namespace. To add
* an artifact, a user must have {@link Action#WRITE} on the namespace in which
* the artifact is being added. If authorization is successful, and
* the artifact is added successfully, then the user gets all {@link Action privileges}
* on the added artifact.
*/
@VisibleForTesting
public ArtifactDetail addArtifact(final Id.Artifact artifactId, final File artifactFile, @Nullable Set<ArtifactRange> parentArtifacts, @Nullable Set<PluginClass> additionalPlugins, Map<String, String> properties) throws Exception {
if (additionalPlugins != null) {
validatePluginSet(additionalPlugins);
}
parentArtifacts = parentArtifacts == null ? Collections.<ArtifactRange>emptySet() : parentArtifacts;
CloseableClassLoader parentClassLoader = null;
EntityImpersonator entityImpersonator = new EntityImpersonator(artifactId.toEntityId(), impersonator);
if (!parentArtifacts.isEmpty()) {
validateParentSet(artifactId, parentArtifacts);
parentClassLoader = createParentClassLoader(artifactId, parentArtifacts, entityImpersonator);
}
try {
ArtifactClasses artifactClasses = inspectArtifact(artifactId, artifactFile, additionalPlugins, parentClassLoader);
ArtifactMeta meta = new ArtifactMeta(artifactClasses, parentArtifacts, properties);
ArtifactDetail artifactDetail = artifactStore.write(artifactId, meta, Files.newInputStreamSupplier(artifactFile), entityImpersonator);
ArtifactDescriptor descriptor = artifactDetail.getDescriptor();
// info hides some fields that are available in detail, such as the location of the artifact
ArtifactInfo artifactInfo = new ArtifactInfo(descriptor.getArtifactId(), artifactDetail.getMeta().getClasses(), artifactDetail.getMeta().getProperties());
// add system metadata for artifacts
writeSystemMetadata(artifactId.toEntityId(), artifactInfo);
return artifactDetail;
} finally {
Closeables.closeQuietly(parentClassLoader);
}
}
use of co.cask.cdap.api.artifact.CloseableClassLoader in project cdap by caskdata.
the class ArtifactClassLoaderFactory method createClassLoader.
/**
* Creates a multi level classloader where each location in the specified iterator corresponds to a classloader whose
* parent is built from the location behind it.
*
* @param artifactLocations the locations of the artifact to create the classloader from
* @return a closeable classloader based off the specified artifacts; 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
* @see #createClassLoader(File)
*/
CloseableClassLoader createClassLoader(final Iterator<Location> artifactLocations, EntityImpersonator entityImpersonator) throws IOException {
if (!artifactLocations.hasNext()) {
throw new IllegalArgumentException("Cannot create a classloader without an artifact.");
}
final Location artifactLocation = artifactLocations.next();
if (!artifactLocations.hasNext()) {
return createClassLoader(artifactLocation, entityImpersonator);
}
try {
final File unpackDir = entityImpersonator.impersonate(new Callable<File>() {
@Override
public File call() throws IOException {
return BundleJarUtil.unJar(artifactLocation, DirUtils.createTempDir(tmpDir));
}
});
final CloseableClassLoader parentClassLoader = createClassLoader(artifactLocations, entityImpersonator);
return new CloseableClassLoader(new DirectoryClassLoader(unpackDir, parentClassLoader, "lib"), new Closeable() {
@Override
public void close() throws IOException {
try {
Closeables.closeQuietly(parentClassLoader);
if (unpackDir.exists()) {
DirUtils.deleteDirectoryContents(unpackDir);
}
} catch (IOException e) {
LOG.warn("Failed to delete directory {}", unpackDir, e);
}
}
});
} catch (Exception e) {
throw Throwables.propagate(e);
}
}
use of co.cask.cdap.api.artifact.CloseableClassLoader in project cdap by caskdata.
the class ArtifactClassLoaderFactory method createClassLoader.
/**
* Create a classloader that loads classes from a directory where an artifact jar has been expanded, 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 unpackDir the directory where the artifact jar has been expanded
* @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(File unpackDir) throws IOException {
ProgramRunner programRunner = 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);
} 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);
}
ProgramClassLoader programClassLoader = null;
if (programRunner instanceof ProgramClassLoaderProvider) {
programClassLoader = new ProgramClassLoader(cConf, unpackDir, ((ProgramClassLoaderProvider) programRunner).createProgramClassLoaderParent());
}
if (programClassLoader == null) {
programClassLoader = new ProgramClassLoader(cConf, unpackDir, FilterClassLoader.create(getClass().getClassLoader()));
}
final ClassLoader finalProgramClassLoader = programClassLoader;
final ProgramRunner finalProgramRunner = programRunner;
return new CloseableClassLoader(programClassLoader, new Closeable() {
@Override
public void close() {
Closeables.closeQuietly((Closeable) finalProgramClassLoader);
if (finalProgramRunner instanceof Closeable) {
Closeables.closeQuietly((Closeable) finalProgramRunner);
}
}
});
}
use of co.cask.cdap.api.artifact.CloseableClassLoader in project cdap by caskdata.
the class ArtifactClassLoaderFactory method createClassLoader.
/**
* Unpack the given {@code artifactLocation} to a temporary directory and call
* {@link #createClassLoader(File)} to create the {@link ClassLoader}.
*
* @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
* @see #createClassLoader(File)
*/
private CloseableClassLoader createClassLoader(final Location artifactLocation, EntityImpersonator entityImpersonator) throws IOException {
try {
final File unpackDir = entityImpersonator.impersonate(new Callable<File>() {
@Override
public File call() throws IOException {
return BundleJarUtil.unJar(artifactLocation, DirUtils.createTempDir(tmpDir));
}
});
final CloseableClassLoader classLoader = createClassLoader(unpackDir);
return new CloseableClassLoader(classLoader, new Closeable() {
@Override
public void close() throws IOException {
try {
Closeables.closeQuietly(classLoader);
if (unpackDir.exists()) {
DirUtils.deleteDirectoryContents(unpackDir);
}
} catch (IOException e) {
LOG.warn("Failed to delete directory {}", unpackDir, e);
}
}
});
} catch (Exception e) {
throw Throwables.propagate(e);
}
}
use of co.cask.cdap.api.artifact.CloseableClassLoader in project cdap by caskdata.
the class ArtifactInspectorTest method inspectAppsAndPlugins.
@Test
public void inspectAppsAndPlugins() throws Exception {
Manifest manifest = new Manifest();
manifest.getMainAttributes().put(ManifestFields.EXPORT_PACKAGE, InspectionApp.class.getPackage().getName());
File appFile = createJar(InspectionApp.class, new File(TMP_FOLDER.newFolder(), "InspectionApp-1.0.0.jar"), manifest);
Id.Artifact artifactId = Id.Artifact.from(Id.Namespace.DEFAULT, "InspectionApp", "1.0.0");
Location artifactLocation = Locations.toLocation(appFile);
try (CloseableClassLoader artifactClassLoader = classLoaderFactory.createClassLoader(ImmutableList.of(artifactLocation).iterator(), new EntityImpersonator(artifactId.toEntityId(), new DefaultImpersonator(CConfiguration.create(), null)))) {
ArtifactClasses classes = artifactInspector.inspectArtifact(artifactId, appFile, artifactClassLoader);
// check app classes
Set<ApplicationClass> expectedApps = ImmutableSet.of(new ApplicationClass(InspectionApp.class.getName(), "", new ReflectionSchemaGenerator(false).generate(InspectionApp.AConfig.class)));
Assert.assertEquals(expectedApps, classes.getApps());
// check plugin classes
PluginClass expectedPlugin = new PluginClass(InspectionApp.PLUGIN_TYPE, InspectionApp.PLUGIN_NAME, InspectionApp.PLUGIN_DESCRIPTION, InspectionApp.AppPlugin.class.getName(), "pluginConf", ImmutableMap.of("y", new PluginPropertyField("y", "", "double", true, true), "isSomething", new PluginPropertyField("isSomething", "", "boolean", true, false)));
Assert.assertEquals(ImmutableSet.of(expectedPlugin), classes.getPlugins());
}
}
Aggregations