Search in sources :

Example 1 with DirectoryClassLoader

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);
    }
}
Also used : DirectoryClassLoader(co.cask.cdap.common.lang.DirectoryClassLoader) Closeable(java.io.Closeable) IOException(java.io.IOException) CloseableClassLoader(co.cask.cdap.api.artifact.CloseableClassLoader) File(java.io.File) IOException(java.io.IOException) Location(org.apache.twill.filesystem.Location)

Example 2 with DirectoryClassLoader

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()));
    }
}
Also used : DirectoryClassLoader(co.cask.cdap.common.lang.DirectoryClassLoader) HttpResponse(co.cask.common.http.HttpResponse) IOException(java.io.IOException) CloseableClassLoader(co.cask.cdap.api.artifact.CloseableClassLoader) File(java.io.File) Location(org.apache.twill.filesystem.Location)

Example 3 with DirectoryClassLoader

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);
    }
}
Also used : ImmutableSet(com.google.common.collect.ImmutableSet) Set(java.util.Set) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet) DatasetTypeId(co.cask.cdap.proto.id.DatasetTypeId) URI(java.net.URI) DatasetModuleMeta(co.cask.cdap.proto.DatasetModuleMeta) DirectoryClassLoader(co.cask.cdap.common.lang.DirectoryClassLoader) TypeConflictException(co.cask.cdap.data2.dataset2.TypeConflictException) DatasetInstanceMDS(co.cask.cdap.data2.datafabric.dataset.service.mds.DatasetInstanceMDS) DatasetSpecification(co.cask.cdap.api.dataset.DatasetSpecification) TransactionExecutor(org.apache.tephra.TransactionExecutor) IOException(java.io.IOException) TransactionFailureException(org.apache.tephra.TransactionFailureException) TypeConflictException(co.cask.cdap.data2.dataset2.TypeConflictException) IOException(java.io.IOException) DatasetTypeMDS(co.cask.cdap.data2.datafabric.dataset.service.mds.DatasetTypeMDS) TransactionFailureException(org.apache.tephra.TransactionFailureException) Collection(java.util.Collection) File(java.io.File) Nullable(javax.annotation.Nullable)

Example 4 with DirectoryClassLoader

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));
}
Also used : DirectoryClassLoader(co.cask.cdap.common.lang.DirectoryClassLoader) CloseableClassLoader(co.cask.cdap.api.artifact.CloseableClassLoader) File(java.io.File)

Aggregations

DirectoryClassLoader (co.cask.cdap.common.lang.DirectoryClassLoader)4 File (java.io.File)4 CloseableClassLoader (co.cask.cdap.api.artifact.CloseableClassLoader)3 IOException (java.io.IOException)3 Location (org.apache.twill.filesystem.Location)2 DatasetSpecification (co.cask.cdap.api.dataset.DatasetSpecification)1 DatasetInstanceMDS (co.cask.cdap.data2.datafabric.dataset.service.mds.DatasetInstanceMDS)1 DatasetTypeMDS (co.cask.cdap.data2.datafabric.dataset.service.mds.DatasetTypeMDS)1 TypeConflictException (co.cask.cdap.data2.dataset2.TypeConflictException)1 DatasetModuleMeta (co.cask.cdap.proto.DatasetModuleMeta)1 DatasetTypeId (co.cask.cdap.proto.id.DatasetTypeId)1 HttpResponse (co.cask.common.http.HttpResponse)1 ImmutableSet (com.google.common.collect.ImmutableSet)1 Closeable (java.io.Closeable)1 URI (java.net.URI)1 Collection (java.util.Collection)1 HashSet (java.util.HashSet)1 LinkedHashSet (java.util.LinkedHashSet)1 Set (java.util.Set)1 Nullable (javax.annotation.Nullable)1