Search in sources :

Example 56 with PluginClass

use of co.cask.cdap.api.plugin.PluginClass in project cdap by caskdata.

the class ArtifactHttpHandler method addArtifact.

@POST
@Path("/namespaces/{namespace-id}/artifacts/{artifact-name}")
@AuditPolicy(AuditDetail.HEADERS)
public BodyConsumer addArtifact(HttpRequest request, HttpResponder responder, @PathParam("namespace-id") final String namespaceId, @PathParam("artifact-name") final String artifactName, @HeaderParam(VERSION_HEADER) final String artifactVersion, @HeaderParam(EXTENDS_HEADER) final String parentArtifactsStr, @HeaderParam(PLUGINS_HEADER) String pluginClasses) throws NamespaceNotFoundException, BadRequestException {
    final NamespaceId namespace = validateAndGetNamespace(namespaceId);
    // and validated there
    if (artifactVersion != null && !artifactVersion.isEmpty()) {
        validateAndGetArtifactId(namespace, artifactName, artifactVersion);
    }
    final Set<ArtifactRange> parentArtifacts = parseExtendsHeader(namespace, parentArtifactsStr);
    final Set<PluginClass> additionalPluginClasses;
    if (pluginClasses == null) {
        additionalPluginClasses = ImmutableSet.of();
    } else {
        try {
            additionalPluginClasses = GSON.fromJson(pluginClasses, PLUGINS_TYPE);
        } catch (JsonParseException e) {
            responder.sendString(HttpResponseStatus.BAD_REQUEST, String.format("%s header '%s' is invalid: %s", PLUGINS_HEADER, pluginClasses, e.getMessage()));
            return null;
        }
    }
    try {
        // copy the artifact contents to local tmp directory
        final File destination = File.createTempFile("artifact-", ".jar", tmpDir);
        return new AbstractBodyConsumer(destination) {

            @Override
            protected void onFinish(HttpResponder responder, File uploadedFile) {
                try {
                    String version = (artifactVersion == null || artifactVersion.isEmpty()) ? getBundleVersion(uploadedFile) : artifactVersion;
                    ArtifactId artifactId = validateAndGetArtifactId(namespace, artifactName, version);
                    // add the artifact to the repo
                    artifactRepository.addArtifact(Id.Artifact.fromEntityId(artifactId), uploadedFile, parentArtifacts, additionalPluginClasses);
                    responder.sendString(HttpResponseStatus.OK, "Artifact added successfully");
                } catch (ArtifactRangeNotFoundException e) {
                    responder.sendString(HttpResponseStatus.NOT_FOUND, e.getMessage());
                } catch (ArtifactAlreadyExistsException e) {
                    responder.sendString(HttpResponseStatus.CONFLICT, e.getMessage());
                } catch (WriteConflictException e) {
                    responder.sendString(HttpResponseStatus.INTERNAL_SERVER_ERROR, "Conflict while writing artifact, please try again.");
                } catch (IOException e) {
                    LOG.error("Exception while trying to write artifact {}-{}-{}.", namespaceId, artifactName, artifactVersion, e);
                    responder.sendString(HttpResponseStatus.INTERNAL_SERVER_ERROR, "Error performing IO while writing artifact.");
                } catch (BadRequestException e) {
                    responder.sendString(HttpResponseStatus.BAD_REQUEST, e.getMessage());
                } catch (UnauthorizedException e) {
                    responder.sendString(HttpResponseStatus.FORBIDDEN, e.getMessage());
                } catch (Exception e) {
                    LOG.error("Error while writing artifact {}-{}-{}", namespaceId, artifactName, artifactVersion, e);
                    responder.sendString(HttpResponseStatus.INTERNAL_SERVER_ERROR, "Error while adding artifact.");
                }
            }

            private String getBundleVersion(File file) throws BadRequestException, IOException {
                try (JarFile jarFile = new JarFile(file)) {
                    Manifest manifest = jarFile.getManifest();
                    if (manifest == null) {
                        throw new BadRequestException("Unable to derive version from artifact because it does not contain a manifest. " + "Please package the jar with a manifest, or explicitly specify the artifact version.");
                    }
                    Attributes attributes = manifest.getMainAttributes();
                    String version = attributes == null ? null : attributes.getValue(ManifestFields.BUNDLE_VERSION);
                    if (version == null) {
                        throw new BadRequestException("Unable to derive version from artifact because manifest does not contain Bundle-Version attribute. " + "Please include Bundle-Version in the manifest, or explicitly specify the artifact version.");
                    }
                    return version;
                } catch (ZipException e) {
                    throw new BadRequestException("Artifact is not in zip format. Please make sure it is a jar file.");
                }
            }
        };
    } catch (IOException e) {
        LOG.error("Exception creating temp file to place artifact {} contents", artifactName, e);
        responder.sendString(HttpResponseStatus.INTERNAL_SERVER_ERROR, "Server error creating temp file for artifact.");
        return null;
    }
}
Also used : ArtifactRangeNotFoundException(co.cask.cdap.common.ArtifactRangeNotFoundException) HttpResponder(co.cask.http.HttpResponder) ArtifactId(co.cask.cdap.proto.id.ArtifactId) ArtifactRange(co.cask.cdap.api.artifact.ArtifactRange) Attributes(java.util.jar.Attributes) ZipException(java.util.zip.ZipException) IOException(java.io.IOException) JsonParseException(com.google.gson.JsonParseException) JarFile(java.util.jar.JarFile) Manifest(java.util.jar.Manifest) InvalidArtifactRangeException(co.cask.cdap.api.artifact.InvalidArtifactRangeException) NamespaceNotFoundException(co.cask.cdap.common.NamespaceNotFoundException) ArtifactNotFoundException(co.cask.cdap.common.ArtifactNotFoundException) ZipException(java.util.zip.ZipException) ArtifactAlreadyExistsException(co.cask.cdap.common.ArtifactAlreadyExistsException) InvocationTargetException(java.lang.reflect.InvocationTargetException) BadRequestException(co.cask.cdap.common.BadRequestException) UnauthorizedException(co.cask.cdap.security.spi.authorization.UnauthorizedException) JsonParseException(com.google.gson.JsonParseException) PluginNotExistsException(co.cask.cdap.internal.app.runtime.plugin.PluginNotExistsException) ArtifactRangeNotFoundException(co.cask.cdap.common.ArtifactRangeNotFoundException) WriteConflictException(co.cask.cdap.internal.app.runtime.artifact.WriteConflictException) JsonSyntaxException(com.google.gson.JsonSyntaxException) IOException(java.io.IOException) NotFoundException(co.cask.cdap.common.NotFoundException) ArtifactAlreadyExistsException(co.cask.cdap.common.ArtifactAlreadyExistsException) AbstractBodyConsumer(co.cask.cdap.common.http.AbstractBodyConsumer) WriteConflictException(co.cask.cdap.internal.app.runtime.artifact.WriteConflictException) UnauthorizedException(co.cask.cdap.security.spi.authorization.UnauthorizedException) BadRequestException(co.cask.cdap.common.BadRequestException) NamespaceId(co.cask.cdap.proto.id.NamespaceId) PluginClass(co.cask.cdap.api.plugin.PluginClass) JarFile(java.util.jar.JarFile) File(java.io.File) Path(javax.ws.rs.Path) AuditPolicy(co.cask.cdap.common.security.AuditPolicy) POST(javax.ws.rs.POST)

Example 57 with PluginClass

use of co.cask.cdap.api.plugin.PluginClass in project cdap by caskdata.

the class DefaultPluginConfigurer method addPlugin.

private Plugin addPlugin(String pluginType, String pluginName, String pluginId, PluginProperties properties, PluginSelector selector) throws PluginNotExistsException {
    PluginWithLocation existing = plugins.get(pluginId);
    if (existing != null) {
        throw new IllegalArgumentException(String.format("Plugin of type %s, name %s was already added as id %s.", existing.getPlugin().getPluginClass().getType(), existing.getPlugin().getPluginClass().getName(), pluginId));
    }
    final Class[] callerClasses = CallerClassSecurityManager.getCallerClasses();
    if (callerClasses.length < 3) {
        // This shouldn't happen as there should be someone calling this method.
        throw new IllegalStateException("Invalid call stack.");
    }
    Set<ArtifactId> parents = new LinkedHashSet<>();
    // 0 is the CallerClassSecurityManager, 1 is this class, hence 2 is the actual caller
    for (int i = 2; i < callerClasses.length; i++) {
        ClassLoader classloader = callerClasses[i].getClassLoader();
        if (classloader instanceof PluginClassLoader) {
            // if this is the first time we've seen this plugin artifact, it must be a new plugin parent.
            co.cask.cdap.api.artifact.ArtifactId pluginCallerArtifactId = ((PluginClassLoader) classloader).getArtifactId();
            parents.add((pluginCallerArtifactId.getScope() == ArtifactScope.SYSTEM ? NamespaceId.SYSTEM : pluginNamespaceId).artifact(pluginCallerArtifactId.getName(), pluginCallerArtifactId.getVersion().getVersion()));
        }
    }
    PluginNotExistsException exception = null;
    for (ArtifactId parentId : Iterables.concat(parents, Collections.singleton(artifactId))) {
        try {
            Map.Entry<ArtifactDescriptor, PluginClass> pluginEntry = pluginFinder.findPlugin(pluginNamespaceId, parentId, pluginType, pluginName, selector);
            Plugin plugin = FindPluginHelper.getPlugin(Iterables.transform(parents, ArtifactId::toApiArtifactId), pluginEntry, properties, pluginType, pluginName, pluginInstantiator);
            plugins.put(pluginId, new PluginWithLocation(plugin, pluginEntry.getKey().getLocation()));
            return plugin;
        } catch (PluginNotExistsException e) {
            // ignore this in case the plugin extends something higher up in the call stack.
            // For example, suppose the app uses pluginA, which uses pluginB. However, pluginB is a plugin that
            // has the app as its parent and not pluginA as its parent. In this case, we want to keep going up the call
            // stack until we get to the app as the parent, which would be able to find plugin B.
            exception = e;
        }
    }
    throw exception == null ? new PluginNotExistsException(pluginNamespaceId, pluginType, pluginName) : exception;
}
Also used : LinkedHashSet(java.util.LinkedHashSet) ArtifactId(co.cask.cdap.proto.id.ArtifactId) PluginNotExistsException(co.cask.cdap.internal.app.runtime.plugin.PluginNotExistsException) ArtifactDescriptor(co.cask.cdap.internal.app.runtime.artifact.ArtifactDescriptor) PluginClassLoader(co.cask.cdap.internal.app.runtime.plugin.PluginClassLoader) PluginClass(co.cask.cdap.api.plugin.PluginClass) PluginClass(co.cask.cdap.api.plugin.PluginClass) HashMap(java.util.HashMap) Map(java.util.Map) PluginClassLoader(co.cask.cdap.internal.app.runtime.plugin.PluginClassLoader) Plugin(co.cask.cdap.api.plugin.Plugin)

Example 58 with PluginClass

use of co.cask.cdap.api.plugin.PluginClass in project cdap by caskdata.

the class ArtifactStore method deleteMeta.

private void deleteMeta(Table table, Id.Artifact artifactId, byte[] oldData) throws IOException {
    // delete old artifact data
    ArtifactCell artifactCell = new ArtifactCell(artifactId);
    table.delete(artifactCell.rowkey, artifactCell.column);
    // delete old plugins
    final ArtifactData oldMeta = GSON.fromJson(Bytes.toString(oldData), ArtifactData.class);
    byte[] artifactColumn = new ArtifactColumn(artifactId).getColumn();
    for (PluginClass pluginClass : oldMeta.meta.getClasses().getPlugins()) {
        // delete metadata for each artifact this plugin extends
        for (ArtifactRange artifactRange : oldMeta.meta.getUsableBy()) {
            // p:{namespace}:{type}:{name}
            PluginKey pluginKey = new PluginKey(artifactRange.getNamespace(), artifactRange.getName(), pluginClass.getType(), pluginClass.getName());
            table.delete(pluginKey.getRowKey(), artifactColumn);
        }
        // Delete the universal plugin row
        if (oldMeta.meta.getUsableBy().isEmpty()) {
            UniversalPluginKey pluginKey = new UniversalPluginKey(artifactId.getNamespace().getId(), pluginClass.getType(), pluginClass.getName());
            table.delete(pluginKey.getRowKey(), artifactColumn);
        }
    }
    // delete old appclass metadata
    for (ApplicationClass appClass : oldMeta.meta.getClasses().getApps()) {
        AppClassKey appClassKey = new AppClassKey(artifactId.getNamespace().toEntityId(), appClass.getClassName());
        table.delete(appClassKey.getRowKey(), artifactColumn);
    }
    try {
        new EntityImpersonator(artifactId.toEntityId(), impersonator).impersonate(new Callable<Void>() {

            @Override
            public Void call() throws Exception {
                Locations.getLocationFromAbsolutePath(locationFactory, oldMeta.getLocationPath()).delete();
                return null;
            }
        });
    } catch (IOException ioe) {
        throw ioe;
    } catch (Exception e) {
        // this should not happen
        throw Throwables.propagate(e);
    }
}
Also used : EntityImpersonator(co.cask.cdap.security.impersonation.EntityImpersonator) ArtifactRange(co.cask.cdap.api.artifact.ArtifactRange) ApplicationClass(co.cask.cdap.api.artifact.ApplicationClass) IOException(java.io.IOException) TransactionFailureException(org.apache.tephra.TransactionFailureException) ArtifactNotFoundException(co.cask.cdap.common.ArtifactNotFoundException) ArtifactAlreadyExistsException(co.cask.cdap.common.ArtifactAlreadyExistsException) TransactionConflictException(org.apache.tephra.TransactionConflictException) PluginNotExistsException(co.cask.cdap.internal.app.runtime.plugin.PluginNotExistsException) DatasetManagementException(co.cask.cdap.api.dataset.DatasetManagementException) IOException(java.io.IOException) PluginClass(co.cask.cdap.api.plugin.PluginClass)

Example 59 with PluginClass

use of co.cask.cdap.api.plugin.PluginClass in project cdap by caskdata.

the class ArtifactStore method getPluginClasses.

/**
 * Get all plugin classes of the given type and name that extend the given parent artifact.
 * Results are returned as a map from plugin artifact to plugins in that artifact.
 *
 * @param namespace the namespace to search for plugins. The system namespace is always included.
 * @param parentArtifactRange the parent artifact range to find plugins for
 * @param type the type of plugin to look for
 * @param name the name of the plugin to look for
 * @param pluginRange the predicate for the plugins
 * @param limit the limit number of the result
 * @param order the order of the result
 * @return an unmodifiable map of plugin artifact to plugin classes of the given type and name, accessible by the
 *         given artifact. The map will never be null, and will never be empty.
 * @throws PluginNotExistsException if no plugin with the given type and name exists in the namespace
 * @throws IOException if there was an exception reading metadata from the metastore
 */
public SortedMap<ArtifactDescriptor, PluginClass> getPluginClasses(NamespaceId namespace, ArtifactRange parentArtifactRange, String type, String name, @Nullable final Predicate<co.cask.cdap.proto.id.ArtifactId> pluginRange, int limit, ArtifactSortOrder order) throws IOException, ArtifactNotFoundException, PluginNotExistsException {
    SortedMap<ArtifactDescriptor, PluginClass> result = Transactionals.execute(transactional, context -> {
        Table metaTable = getMetaTable(context);
        List<ArtifactDetail> parentArtifactDetails = getArtifacts(metaTable, parentArtifactRange, Integer.MAX_VALUE, null);
        if (parentArtifactDetails.isEmpty()) {
            throw new ArtifactNotFoundException(parentArtifactRange.getNamespace(), parentArtifactRange.getName());
        }
        SortedMap<ArtifactDescriptor, PluginClass> plugins = order == ArtifactSortOrder.DESC ? new TreeMap<>(Collections.reverseOrder()) : new TreeMap<>();
        List<Id.Artifact> parentArtifacts = new ArrayList<>();
        for (ArtifactDetail parentArtifactDetail : parentArtifactDetails) {
            parentArtifacts.add(Id.Artifact.from(Id.Namespace.from(parentArtifactRange.getNamespace()), parentArtifactDetail.getDescriptor().getArtifactId()));
            Set<PluginClass> parentPlugins = parentArtifactDetail.getMeta().getClasses().getPlugins();
            for (PluginClass pluginClass : parentPlugins) {
                if (pluginClass.getName().equals(name) && pluginClass.getType().equals(type)) {
                    plugins.put(parentArtifactDetail.getDescriptor(), pluginClass);
                    break;
                }
            }
        }
        // Add all plugins that extends from the given set of parents
        PluginKey pluginKey = new PluginKey(parentArtifactRange.getNamespace(), parentArtifactRange.getName(), type, name);
        Row row = metaTable.get(pluginKey.getRowKey());
        if (!row.isEmpty()) {
            addPluginsInRangeToMap(namespace, parentArtifacts, row.getColumns(), plugins, pluginRange, limit);
        }
        // Add all universal plugins
        for (String ns : Arrays.asList(namespace.getNamespace(), NamespaceId.SYSTEM.getNamespace())) {
            UniversalPluginKey universalPluginKey = new UniversalPluginKey(ns, type, name);
            row = metaTable.get(universalPluginKey.getRowKey());
            if (!row.isEmpty()) {
                addPluginsInRangeToMap(namespace, parentArtifacts, row.getColumns(), plugins, pluginRange, limit);
            }
        }
        return Collections.unmodifiableSortedMap(plugins);
    }, IOException.class, ArtifactNotFoundException.class);
    if (result.isEmpty()) {
        throw new PluginNotExistsException(new NamespaceId(parentArtifactRange.getNamespace()), type, name);
    }
    return result;
}
Also used : Table(co.cask.cdap.api.dataset.table.Table) ArrayList(java.util.ArrayList) PluginNotExistsException(co.cask.cdap.internal.app.runtime.plugin.PluginNotExistsException) Row(co.cask.cdap.api.dataset.table.Row) NamespaceId(co.cask.cdap.proto.id.NamespaceId) PluginClass(co.cask.cdap.api.plugin.PluginClass) ArtifactNotFoundException(co.cask.cdap.common.ArtifactNotFoundException)

Example 60 with PluginClass

use of co.cask.cdap.api.plugin.PluginClass in project cdap by caskdata.

the class PluginService method getPluginEndpoint.

private PluginEndpoint getPluginEndpoint(NamespaceId namespace, ArtifactDetail artifactDetail, String pluginType, String pluginName, ArtifactDescriptor parentArtifactDescriptor, Set<ArtifactRange> parentArtifactRanges, String methodName) throws NotFoundException, IOException, ClassNotFoundException {
    Id.Artifact artifactId = Id.Artifact.from(Id.Namespace.fromEntityId(namespace), artifactDetail.getDescriptor().getArtifactId());
    Set<PluginClass> pluginClasses = artifactDetail.getMeta().getClasses().getPlugins();
    PluginClass pluginClass = null;
    for (PluginClass plugin : pluginClasses) {
        if (plugin.getName().equals(pluginName) && plugin.getType().equals(pluginType)) {
            // plugin type and name matched, next check for endpoint method presence
            if (plugin.getEndpoints() == null || !plugin.getEndpoints().contains(methodName)) {
                throw new NotFoundException(String.format("Plugin with type: %s name: %s found, " + "but Endpoint %s was not found", pluginType, pluginName, methodName));
            }
            pluginClass = plugin;
        }
    }
    if (pluginClass == null) {
        throw new NotFoundException(String.format("No Plugin with type : %s, name: %s was found", pluginType, pluginName));
    }
    // initialize parent classloader and plugin instantiator
    Instantiators instantiators = this.instantiators.getUnchecked(parentArtifactDescriptor);
    PluginInstantiator pluginInstantiator = instantiators.getPluginInstantiator(artifactDetail, artifactId.toArtifactId());
    // we pass the parent artifact to endpoint plugin context,
    // as plugin method will use this context to load other plugins.
    DefaultEndpointPluginContext defaultEndpointPluginContext = new DefaultEndpointPluginContext(namespace, authArtifactRepository, pluginInstantiator, parentArtifactRanges);
    return getPluginEndpoint(pluginInstantiator, artifactId, pluginClass, methodName, defaultEndpointPluginContext);
}
Also used : DefaultEndpointPluginContext(co.cask.cdap.internal.app.runtime.DefaultEndpointPluginContext) ArtifactNotFoundException(co.cask.cdap.common.ArtifactNotFoundException) NotFoundException(co.cask.cdap.common.NotFoundException) ArtifactId(co.cask.cdap.api.artifact.ArtifactId) Id(co.cask.cdap.common.id.Id) NamespaceId(co.cask.cdap.proto.id.NamespaceId) PluginClass(co.cask.cdap.api.plugin.PluginClass)

Aggregations

PluginClass (co.cask.cdap.api.plugin.PluginClass)76 PluginPropertyField (co.cask.cdap.api.plugin.PluginPropertyField)46 HashMap (java.util.HashMap)37 Test (org.junit.Test)24 ArtifactId (co.cask.cdap.proto.id.ArtifactId)22 NamespaceId (co.cask.cdap.proto.id.NamespaceId)21 ArtifactRange (co.cask.cdap.api.artifact.ArtifactRange)19 ArtifactVersion (co.cask.cdap.api.artifact.ArtifactVersion)17 Id (co.cask.cdap.common.id.Id)17 Set (java.util.Set)14 ImmutableSet (com.google.common.collect.ImmutableSet)13 PluginNotExistsException (co.cask.cdap.internal.app.runtime.plugin.PluginNotExistsException)11 Map (java.util.Map)11 ArtifactNotFoundException (co.cask.cdap.common.ArtifactNotFoundException)10 SortedMap (java.util.SortedMap)10 ArtifactId (co.cask.cdap.api.artifact.ArtifactId)9 File (java.io.File)9 ApplicationClass (co.cask.cdap.api.artifact.ApplicationClass)8 HashSet (java.util.HashSet)8 ImmutableMap (com.google.common.collect.ImmutableMap)7