use of co.cask.cdap.common.lang.DirectoryClassLoader 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.common.lang.DirectoryClassLoader in project cdap by caskdata.
the class DefaultArtifactManager method createAndGetClassLoader.
private CloseableClassLoader createAndGetClassLoader(HttpRequest httpRequest, ArtifactInfo artifactInfo, @Nullable ClassLoader parentClassLoader) throws IOException {
HttpResponse httpResponse = remoteClient.execute(httpRequest);
if (httpResponse.getResponseCode() == HttpResponseStatus.NOT_FOUND.getCode()) {
throw new IOException("Could not get artifact detail, endpoint not found");
}
if (httpResponse.getResponseCode() == 200) {
String path = httpResponse.getResponseBodyAsString();
Location location = Locations.getLocationFromAbsolutePath(locationFactory, path);
if (!location.exists()) {
throw new IOException(String.format("Artifact Location does not exist %s for artifact %s version %s", path, artifactInfo.getName(), artifactInfo.getVersion()));
}
File unpackedDir = DirUtils.createTempDir(tmpDir);
BundleJarUtil.unJar(location, unpackedDir);
DirectoryClassLoader directoryClassLoader = new DirectoryClassLoader(unpackedDir, parentClassLoader == null ? bootstrapClassLoader : parentClassLoader);
return new CloseableClassLoader(directoryClassLoader, new ClassLoaderCleanup(directoryClassLoader, unpackedDir));
} else {
throw new IOException(String.format("Exception while getting artifacts list %s", httpResponse.getResponseBodyAsString()));
}
}
use of co.cask.cdap.common.lang.DirectoryClassLoader in project cdap by caskdata.
the class DatasetTypeManager method addModule.
/**
* Add datasets module in a namespace
*
* @param datasetModuleId the {@link DatasetModuleId} to add
* @param className module class
* @param jarLocation location of the module jar
* @param force if true, an update will be allowed even if there are conflicts with other modules, or if
* removal of a type would break other modules' dependencies.
*/
public void addModule(final DatasetModuleId datasetModuleId, final String className, final Location jarLocation, final boolean force) throws DatasetModuleConflictException {
LOG.debug("adding module: {}, className: {}, jarLocation: {}", datasetModuleId, className, jarLocation == null ? "[local]" : jarLocation);
try {
final DatasetTypeMDS datasetTypeMDS = datasetCache.getDataset(DatasetMetaTableUtil.META_TABLE_NAME);
final DatasetInstanceMDS datasetInstanceMDS = datasetCache.getDataset(DatasetMetaTableUtil.INSTANCE_TABLE_NAME);
txExecutorFactory.createExecutor(datasetCache).execute(new TransactionExecutor.Subroutine() {
@Override
public void apply() throws Exception {
// 1. get existing module with all its types
DatasetModuleMeta existing = datasetTypeMDS.getModule(datasetModuleId);
DependencyTrackingRegistry reg;
// 2. unpack jar and create class loader
File unpackedLocation = Files.createTempDirectory(Files.createDirectories(systemTempPath), datasetModuleId.getEntityName()).toFile();
DirectoryClassLoader cl = null;
try {
// NOTE: if jarLocation is null, we assume that this is a system module, ie. always present in classpath
if (jarLocation != null) {
BundleJarUtil.unJar(jarLocation, unpackedLocation);
cl = new DirectoryClassLoader(unpackedLocation, cConf.get(Constants.AppFabric.PROGRAM_EXTRA_CLASSPATH), FilterClassLoader.create(getClass().getClassLoader()), "lib");
}
reg = new DependencyTrackingRegistry(datasetModuleId, datasetTypeMDS, cl, force);
// 3. register the new module while tracking dependencies.
// this will fail if a type exists in a different module
DatasetDefinitionRegistries.register(className, cl, reg);
} catch (TypeConflictException e) {
// type conflict from the registry, we want to throw that as is
throw e;
} catch (Exception e) {
LOG.error("Could not instantiate instance of dataset module class {} for module {} using jarLocation {}", className, datasetModuleId, jarLocation);
throw Throwables.propagate(e);
} finally {
if (cl != null) {
// Close the ProgramClassLoader
Closeables.closeQuietly(cl);
}
try {
DirUtils.deleteDirectoryContents(unpackedLocation);
} catch (IOException e) {
LOG.warn("Failed to delete directory {}", unpackedLocation, e);
}
}
// 4. determine whether any type were removed from the module, and whether any other modules depend on them
if (existing != null) {
Set<String> removedTypes = new HashSet<>(existing.getTypes());
removedTypes.removeAll(reg.getTypes());
// TODO (CDAP-6294): track dependencies at the type level
if (!force && !removedTypes.isEmpty() && !existing.getUsedByModules().isEmpty()) {
throw new DatasetModuleConflictException(String.format("Cannot update module '%s' to remove types %s: Modules %s may depend on it. Delete them first", datasetModuleId, removedTypes, existing.getUsedByModules()));
}
Collection<DatasetSpecification> instances = datasetInstanceMDS.getByTypes(datasetModuleId.getParent(), removedTypes);
if (!instances.isEmpty()) {
throw new DatasetModuleConflictException(String.format("Attempt to remove dataset types %s from module '%s' that have existing instances: %s. " + "Delete them first.", removedTypes, datasetModuleId, Iterables.toString(Iterables.transform(instances, new Function<DatasetSpecification, String>() {
@Nullable
@Override
public String apply(@Nullable DatasetSpecification input) {
return input.getName() + ":" + input.getType();
}
}))));
}
}
// NOTE: we use set to avoid duplicated dependencies
// NOTE: we use LinkedHashSet to preserve order in which dependencies must be loaded
Set<String> moduleDependencies = new LinkedHashSet<String>();
for (DatasetTypeId usedType : reg.getUsedTypes()) {
DatasetModuleMeta usedModule = datasetTypeMDS.getModuleByType(usedType);
Preconditions.checkState(usedModule != null, String.format("Found a null used module for type %s for while adding module %s", usedType, datasetModuleId));
// adding all used types and the module itself, in this very order to keep the order of loading modules
// for instantiating a type
moduleDependencies.addAll(usedModule.getUsesModules());
boolean added = moduleDependencies.add(usedModule.getName());
if (added) {
// also adding this module as a dependent for all modules it uses
usedModule.addUsedByModule(datasetModuleId.getEntityName());
datasetTypeMDS.writeModule(usedType.getParent(), usedModule);
}
}
URI jarURI = jarLocation == null ? null : jarLocation.toURI();
DatasetModuleMeta moduleMeta = existing == null ? new DatasetModuleMeta(datasetModuleId.getEntityName(), className, jarURI, reg.getTypes(), Lists.newArrayList(moduleDependencies)) : new DatasetModuleMeta(datasetModuleId.getEntityName(), className, jarURI, reg.getTypes(), Lists.newArrayList(moduleDependencies), Lists.newArrayList(existing.getUsedByModules()));
datasetTypeMDS.writeModule(datasetModuleId.getParent(), moduleMeta);
}
});
} catch (TransactionFailureException e) {
Throwable cause = e.getCause();
if (cause != null) {
if (cause instanceof DatasetModuleConflictException) {
throw (DatasetModuleConflictException) cause;
} else if (cause instanceof TypeConflictException) {
throw new DatasetModuleConflictException(cause.getMessage(), cause);
}
}
throw Throwables.propagate(e);
} catch (Exception e) {
LOG.error("Operation failed", e);
throw Throwables.propagate(e);
}
}
use of co.cask.cdap.common.lang.DirectoryClassLoader in project cdap by caskdata.
the class AbstractArtifactManager method createClassLoader.
/**
* Create a class loader with artifact jar unpacked contents and parent for this classloader is the supplied
* parentClassLoader, if that parent classloader is null, bootstrap classloader is used as parent.
* This is a closeable classloader, caller should call close when he is done using it, during close directory
* cleanup will be performed.
*
* @param artifactInfo artifact info whose artifact will be unpacked to create classloader
* @param parentClassLoader optional parent classloader, if null bootstrap classloader will be used
* @return CloseableClassLoader call close on this CloseableClassLoader for cleanup
* @throws IOException if artifact is not found or there were any error while getting artifact
*/
@Override
public CloseableClassLoader createClassLoader(ArtifactInfo artifactInfo, @Nullable ClassLoader parentClassLoader) throws IOException {
File unpackedDir = DirUtils.createTempDir(tmpDir);
BundleJarUtil.unJar(getArtifactLocation(artifactInfo), unpackedDir);
DirectoryClassLoader directoryClassLoader = new DirectoryClassLoader(unpackedDir, parentClassLoader == null ? bootstrapClassLoader : parentClassLoader, "lib");
return new CloseableClassLoader(directoryClassLoader, new ClassLoaderCleanup(directoryClassLoader, unpackedDir));
}
Aggregations