Search in sources :

Example 6 with ArtifactAlreadyExistsException

use of io.cdap.cdap.common.ArtifactAlreadyExistsException in project cdap by caskdata.

the class AppLifecycleHttpHandler method deployApplication.

private BodyConsumer deployApplication(final HttpResponder responder, final NamespaceId namespace, final String appId, final String archiveName, final String configString, @Nullable final String ownerPrincipal, final boolean updateSchedules) throws IOException {
    Id.Namespace idNamespace = Id.Namespace.fromEntityId(namespace);
    Location namespaceHomeLocation = namespacePathLocator.get(namespace);
    if (!namespaceHomeLocation.exists()) {
        String msg = String.format("Home directory %s for namespace %s not found", namespaceHomeLocation, namespace.getNamespace());
        LOG.error(msg);
        responder.sendString(HttpResponseStatus.NOT_FOUND, msg);
        return null;
    }
    if (archiveName == null || archiveName.isEmpty()) {
        responder.sendString(HttpResponseStatus.BAD_REQUEST, String.format("%s header not present. Please include the header and set its value to the jar name.", ARCHIVE_NAME_HEADER), new DefaultHttpHeaders().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE));
        return null;
    }
    // TODO: (CDAP-3258) error handling needs to be refactored here, should be able just to throw the exception,
    // but the caller catches all exceptions and responds with a 500
    final Id.Artifact artifactId;
    try {
        artifactId = Id.Artifact.parse(idNamespace, archiveName);
    } catch (IllegalArgumentException e) {
        responder.sendString(HttpResponseStatus.BAD_REQUEST, e.getMessage());
        return null;
    }
    KerberosPrincipalId ownerPrincipalId = ownerPrincipal == null ? null : new KerberosPrincipalId(ownerPrincipal);
    // Store uploaded content to a local temp file
    String namespacesDir = configuration.get(Constants.Namespace.NAMESPACES_DIR);
    File localDataDir = new File(configuration.get(Constants.CFG_LOCAL_DATA_DIR));
    File namespaceBase = new File(localDataDir, namespacesDir);
    File tempDir = new File(new File(namespaceBase, namespace.getNamespace()), configuration.get(Constants.AppFabric.TEMP_DIR)).getAbsoluteFile();
    if (!DirUtils.mkdirs(tempDir)) {
        throw new IOException("Could not create temporary directory at: " + tempDir);
    }
    final KerberosPrincipalId finalOwnerPrincipalId = ownerPrincipalId;
    return new AbstractBodyConsumer(File.createTempFile("app-", ".jar", tempDir)) {

        @Override
        protected void onFinish(HttpResponder responder, File uploadedFile) {
            try {
                // deploy app
                ApplicationWithPrograms app = applicationLifecycleService.deployAppAndArtifact(namespace, appId, artifactId, uploadedFile, configString, finalOwnerPrincipalId, createProgramTerminator(), updateSchedules);
                LOG.info("Successfully deployed app {} in namespace {} from artifact {} with configuration {} and " + "principal {}", app.getApplicationId().getApplication(), namespace.getNamespace(), artifactId, configString, finalOwnerPrincipalId);
                responder.sendString(HttpResponseStatus.OK, String.format("Successfully deployed app %s", app.getApplicationId().getApplication()));
            } catch (InvalidArtifactException e) {
                responder.sendString(HttpResponseStatus.BAD_REQUEST, e.getMessage());
            } catch (ArtifactAlreadyExistsException e) {
                responder.sendString(HttpResponseStatus.CONFLICT, String.format("Artifact '%s' already exists. Please use the API that creates an application from an existing artifact. " + "If you are trying to replace the artifact, please delete it and then try again.", artifactId));
            } catch (WriteConflictException e) {
                // don't really expect this to happen. It means after multiple retries there were still write conflicts.
                LOG.warn("Write conflict while trying to add artifact {}.", artifactId, e);
                responder.sendString(HttpResponseStatus.INTERNAL_SERVER_ERROR, "Write conflict while adding artifact. This can happen if multiple requests to add " + "the same artifact occur simultaneously. Please try again.");
            } catch (UnauthorizedException e) {
                responder.sendString(HttpResponseStatus.FORBIDDEN, e.getMessage());
            } catch (ConflictException e) {
                responder.sendString(HttpResponseStatus.CONFLICT, e.getMessage());
            } catch (Exception e) {
                LOG.error("Deploy failure", e);
                responder.sendString(HttpResponseStatus.BAD_REQUEST, e.getMessage());
            }
        }
    };
}
Also used : HttpResponder(io.cdap.http.HttpResponder) WriteConflictException(io.cdap.cdap.internal.app.runtime.artifact.WriteConflictException) ConflictException(io.cdap.cdap.common.ConflictException) IOException(java.io.IOException) ApplicationNotFoundException(io.cdap.cdap.common.ApplicationNotFoundException) UnauthorizedException(io.cdap.cdap.security.spi.authorization.UnauthorizedException) NamespaceNotFoundException(io.cdap.cdap.common.NamespaceNotFoundException) WriteConflictException(io.cdap.cdap.internal.app.runtime.artifact.WriteConflictException) DatasetManagementException(io.cdap.cdap.api.dataset.DatasetManagementException) IOException(java.io.IOException) ConflictException(io.cdap.cdap.common.ConflictException) NotImplementedException(io.cdap.cdap.common.NotImplementedException) AccessException(io.cdap.cdap.api.security.AccessException) InvalidArtifactException(io.cdap.cdap.common.InvalidArtifactException) ArtifactAlreadyExistsException(io.cdap.cdap.common.ArtifactAlreadyExistsException) NotFoundException(io.cdap.cdap.common.NotFoundException) ServiceException(io.cdap.cdap.common.ServiceException) JsonSyntaxException(com.google.gson.JsonSyntaxException) BadRequestException(io.cdap.cdap.common.BadRequestException) ArtifactNotFoundException(io.cdap.cdap.common.ArtifactNotFoundException) ArtifactAlreadyExistsException(io.cdap.cdap.common.ArtifactAlreadyExistsException) DefaultHttpHeaders(io.netty.handler.codec.http.DefaultHttpHeaders) AbstractBodyConsumer(io.cdap.cdap.common.http.AbstractBodyConsumer) WriteConflictException(io.cdap.cdap.internal.app.runtime.artifact.WriteConflictException) ApplicationWithPrograms(io.cdap.cdap.internal.app.deploy.pipeline.ApplicationWithPrograms) UnauthorizedException(io.cdap.cdap.security.spi.authorization.UnauthorizedException) Id(io.cdap.cdap.common.id.Id) ApplicationId(io.cdap.cdap.proto.id.ApplicationId) NamespaceId(io.cdap.cdap.proto.id.NamespaceId) EntityId(io.cdap.cdap.proto.id.EntityId) ProgramId(io.cdap.cdap.proto.id.ProgramId) KerberosPrincipalId(io.cdap.cdap.proto.id.KerberosPrincipalId) KerberosPrincipalId(io.cdap.cdap.proto.id.KerberosPrincipalId) File(java.io.File) InvalidArtifactException(io.cdap.cdap.common.InvalidArtifactException) Location(org.apache.twill.filesystem.Location)

Example 7 with ArtifactAlreadyExistsException

use of io.cdap.cdap.common.ArtifactAlreadyExistsException in project cdap by caskdata.

the class ArtifactStore method write.

/**
 * Write the artifact and its metadata to the store. Once added, artifacts cannot be changed unless they are
 * snapshot versions.
 *
 * @param artifactId the id of the artifact to add
 * @param artifactMeta the metadata for the artifact
 * @param artifactContent the file containing the content of the artifact
 * @return detail about the newly added artifact
 * @throws ArtifactAlreadyExistsException if a non-snapshot version of the artifact already exists
 * @throws IOException if there was an exception persisting the artifact contents to the filesystem,
 *                     of persisting the artifact metadata to the metastore
 */
public ArtifactDetail write(Id.Artifact artifactId, ArtifactMeta artifactMeta, File artifactContent, EntityImpersonator entityImpersonator) throws ArtifactAlreadyExistsException, IOException {
    // if we're not a snapshot version, check that the artifact doesn't exist already.
    if (!artifactId.getVersion().isSnapshot()) {
        TransactionRunners.run(transactionRunner, context -> {
            StructuredTable table = getTable(context, StoreDefinition.ArtifactStore.ARTIFACT_DATA_TABLE);
            ArtifactCell artifactCell = new ArtifactCell(artifactId);
            if (table.read(artifactCell.keys).isPresent()) {
                throw new ArtifactAlreadyExistsException(artifactId.toEntityId());
            }
        }, ArtifactAlreadyExistsException.class, IOException.class);
    }
    final Location destination;
    try {
        destination = copyFileToDestination(artifactId, artifactContent, entityImpersonator);
    } catch (Exception e) {
        Throwables.propagateIfInstanceOf(e, IOException.class);
        throw Throwables.propagate(e);
    }
    // now try and write the metadata for the artifact
    try {
        transactionRunner.run(context -> {
            // we have to check that the metadata doesn't exist again since somebody else may have written
            // the artifact while we were copying the artifact to the filesystem.
            StructuredTable artifactDataTable = getTable(context, StoreDefinition.ArtifactStore.ARTIFACT_DATA_TABLE);
            ArtifactCell artifactCell = new ArtifactCell(artifactId);
            Optional<StructuredRow> optional = artifactDataTable.read(artifactCell.keys);
            boolean isSnapshot = artifactId.getVersion().isSnapshot();
            if (optional.isPresent() && !isSnapshot) {
                // non-snapshot artifacts are immutable. If there is existing metadata, stop here.
                throw new ArtifactAlreadyExistsException(artifactId.toEntityId());
            }
            ArtifactData data = new ArtifactData(destination, artifactMeta);
            // this means cleaning up the old jar, and deleting plugin and app rows.
            if (optional.isPresent()) {
                deleteMeta(context, artifactId, GSON.fromJson(optional.get().getString(StoreDefinition.ArtifactStore.ARTIFACT_DATA_FIELD), ArtifactData.class));
            }
            // write artifact metadata
            writeMeta(context, artifactId, data);
        });
        return new ArtifactDetail(new ArtifactDescriptor(artifactId.getNamespace().getId(), artifactId.toArtifactId(), destination), artifactMeta);
    } catch (TransactionException e) {
        destination.delete();
        // should throw WriteConflictException(artifactId) on transaction conflict
        throw TransactionRunners.propagate(e, ArtifactAlreadyExistsException.class, IOException.class);
    }
}
Also used : StructuredTable(io.cdap.cdap.spi.data.StructuredTable) StructuredRow(io.cdap.cdap.spi.data.StructuredRow) IOException(java.io.IOException) ArtifactAlreadyExistsException(io.cdap.cdap.common.ArtifactAlreadyExistsException) TransactionException(io.cdap.cdap.spi.data.transaction.TransactionException) PluginNotExistsException(io.cdap.cdap.internal.app.runtime.plugin.PluginNotExistsException) IOException(java.io.IOException) TableNotFoundException(io.cdap.cdap.spi.data.TableNotFoundException) ArtifactNotFoundException(io.cdap.cdap.common.ArtifactNotFoundException) TransactionException(io.cdap.cdap.spi.data.transaction.TransactionException) ArtifactAlreadyExistsException(io.cdap.cdap.common.ArtifactAlreadyExistsException) Location(org.apache.twill.filesystem.Location)

Example 8 with ArtifactAlreadyExistsException

use of io.cdap.cdap.common.ArtifactAlreadyExistsException in project cdap by caskdata.

the class HubPackage method installPlugin.

/**
 * Downloads the plugin from the given URL and installs it in the artifact repository
 *
 * @param url URL that points to the plugin directory on the hub
 * @param artifactRepository {@link ArtifactRepository} in which the plugin will be installed
 * @param tmpDir temporary directory where plugin jar is downloaded from the hub
 */
public void installPlugin(URL url, ArtifactRepository artifactRepository, File tmpDir) throws Exception {
    // Deserialize spec.json
    URL specURL = new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getPath() + "/packages/" + name + "/" + version + "/spec.json");
    Spec spec = GSON.fromJson(HttpClients.doGetAsString(specURL), Spec.class);
    for (Spec.Action action : spec.getActions()) {
        // See https://cdap.atlassian.net/wiki/spaces/DOCS/pages/554401840/Hub+API?src=search#one_step_deploy_plugin
        if (!action.getType().equals("one_step_deploy_plugin")) {
            continue;
        }
        String configFilename = action.getConfigFilename();
        if (configFilename == null) {
            LOG.warn("Ignoring plugin {} due to missing config", name);
            continue;
        }
        URL configURL = new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getPath() + Joiner.on("/").join(Arrays.asList("/packages", name, version, configFilename)));
        // Download plugin json from hub
        JsonObject jsonObj = GSON.fromJson(HttpClients.doGetAsString(configURL), JsonObject.class);
        List<String> parents = GSON.fromJson(jsonObj.get("parents"), new TypeToken<List<String>>() {
        }.getType());
        String jarName = action.getJarName();
        if (jarName == null) {
            LOG.warn("Ignoring plugin {} due to missing jar", name);
            continue;
        }
        // Download plugin jar from hub
        File destination = File.createTempFile("artifact-", ".jar", tmpDir);
        FileChannel channel = new FileOutputStream(destination, false).getChannel();
        URL jarURL = new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getPath() + Joiner.on("/").join(Arrays.asList("/packages", name, version, jarName)));
        HttpRequest request = HttpRequest.get(jarURL).withContentConsumer(new HttpContentConsumer() {

            @Override
            public boolean onReceived(ByteBuffer buffer) {
                try {
                    channel.write(buffer);
                } catch (IOException e) {
                    LOG.error("Failed write to file {}", destination);
                    return false;
                }
                return true;
            }

            @Override
            public void onFinished() {
                Closeables.closeQuietly(channel);
            }
        }).build();
        HttpClients.executeStreamingRequest(request);
        Set<ArtifactRange> parentArtifacts = new HashSet<>();
        for (String parent : parents) {
            try {
                // try parsing it as a namespaced range like system:cdap-data-pipeline[6.3 1.1,7.0.0)
                parentArtifacts.add(ArtifactRanges.parseArtifactRange(parent));
            } catch (InvalidArtifactRangeException e) {
                // if this failed, try parsing as a non-namespaced range like cdap-data-pipeline[6.3 1.1,7.0.0)
                parentArtifacts.add(ArtifactRanges.parseArtifactRange(NamespaceId.DEFAULT.getNamespace(), parent));
            }
        }
        // add the artifact to the repo
        io.cdap.cdap.proto.id.ArtifactId artifactId = NamespaceId.DEFAULT.artifact(name, version);
        Id.Artifact artifact = Id.Artifact.fromEntityId(artifactId);
        try {
            artifactRepository.addArtifact(artifact, destination, parentArtifacts, ImmutableSet.of());
        } catch (ArtifactAlreadyExistsException e) {
            LOG.debug("Artifact artifact {}-{} already exists", name, version);
        }
        Map<String, String> properties = GSON.fromJson(jsonObj.get("properties"), new TypeToken<Map<String, String>>() {
        }.getType());
        artifactRepository.writeArtifactProperties(Id.Artifact.fromEntityId(artifactId), properties);
        if (!java.nio.file.Files.deleteIfExists(Paths.get(destination.getPath()))) {
            LOG.warn("Failed to cleanup file {}", destination);
        }
    }
}
Also used : HttpContentConsumer(io.cdap.common.http.HttpContentConsumer) ArtifactRange(io.cdap.cdap.api.artifact.ArtifactRange) JsonObject(com.google.gson.JsonObject) URL(java.net.URL) ArtifactAlreadyExistsException(io.cdap.cdap.common.ArtifactAlreadyExistsException) HashSet(java.util.HashSet) HttpRequest(io.cdap.common.http.HttpRequest) FileChannel(java.nio.channels.FileChannel) InvalidArtifactRangeException(io.cdap.cdap.api.artifact.InvalidArtifactRangeException) IOException(java.io.IOException) ByteBuffer(java.nio.ByteBuffer) TypeToken(io.cdap.cdap.internal.guava.reflect.TypeToken) FileOutputStream(java.io.FileOutputStream) NamespaceId(io.cdap.cdap.proto.id.NamespaceId) Id(io.cdap.cdap.common.id.Id) File(java.io.File)

Example 9 with ArtifactAlreadyExistsException

use of io.cdap.cdap.common.ArtifactAlreadyExistsException in project cdap by caskdata.

the class ArtifactClient method add.

/**
 * Add an artifact.
 *
 * @param namespace the namespace to add the artifact to
 * @param artifactName the name of the artifact to add
 * @param artifactContents a provider for the contents of the artifact
 * @param artifactVersion the version of the artifact to add. If null, the version will be derived from the
 *                        manifest of the artifact
 * @param parentArtifacts the set of artifacts this artifact extends
 * @param additionalPlugins the set of plugins contained in the artifact that cannot be determined
 *                          through jar inspection. This set should include any classes that are plugins but could
 *                          not be annotated as such. For example, 3rd party classes like jdbc drivers fall into
 *                          this category.
 * @throws ArtifactAlreadyExistsException if the artifact already exists
 * @throws BadRequestException if the request is invalid. For example, if the artifact name or version is invalid
 * @throws ArtifactRangeNotFoundException if the parent artifacts do not exist
 * @throws IOException if a network error occurred
 * @throws UnauthenticatedException if the request is not authorized successfully in the gateway server
 */
public void add(NamespaceId namespace, String artifactName, ContentProvider<? extends InputStream> artifactContents, @Nullable String artifactVersion, @Nullable Set<ArtifactRange> parentArtifacts, @Nullable Set<PluginClass> additionalPlugins) throws ArtifactAlreadyExistsException, BadRequestException, IOException, UnauthenticatedException, ArtifactRangeNotFoundException, UnauthorizedException {
    URL url = config.resolveNamespacedURLV3(namespace, String.format("artifacts/%s", artifactName));
    HttpRequest.Builder requestBuilder = HttpRequest.post(url);
    if (artifactVersion != null) {
        requestBuilder.addHeader("Artifact-Version", artifactVersion);
    }
    if (parentArtifacts != null && !parentArtifacts.isEmpty()) {
        requestBuilder.addHeader("Artifact-Extends", Joiner.on('/').join(parentArtifacts));
    }
    if (additionalPlugins != null && !additionalPlugins.isEmpty()) {
        requestBuilder.addHeader("Artifact-Plugins", GSON.toJson(additionalPlugins));
    }
    HttpRequest request = requestBuilder.withBody(artifactContents).build();
    HttpResponse response = restClient.execute(request, config.getAccessToken(), HttpURLConnection.HTTP_CONFLICT, HttpURLConnection.HTTP_BAD_REQUEST, HttpURLConnection.HTTP_NOT_FOUND);
    int responseCode = response.getResponseCode();
    if (responseCode == HttpURLConnection.HTTP_CONFLICT) {
        throw new ArtifactAlreadyExistsException(response.getResponseBodyAsString());
    } else if (responseCode == HttpURLConnection.HTTP_BAD_REQUEST) {
        throw new BadRequestException(response.getResponseBodyAsString());
    } else if (responseCode == HttpURLConnection.HTTP_NOT_FOUND) {
        throw new ArtifactRangeNotFoundException(parentArtifacts);
    }
}
Also used : HttpRequest(io.cdap.common.http.HttpRequest) ArtifactRangeNotFoundException(io.cdap.cdap.common.ArtifactRangeNotFoundException) ArtifactAlreadyExistsException(io.cdap.cdap.common.ArtifactAlreadyExistsException) HttpResponse(io.cdap.common.http.HttpResponse) BadRequestException(io.cdap.cdap.common.BadRequestException) URL(java.net.URL)

Example 10 with ArtifactAlreadyExistsException

use of io.cdap.cdap.common.ArtifactAlreadyExistsException in project cdap by cdapio.

the class ArtifactClient method add.

/**
 * Add an artifact.
 *
 * @param namespace the namespace to add the artifact to
 * @param artifactName the name of the artifact to add
 * @param artifactContents a provider for the contents of the artifact
 * @param artifactVersion the version of the artifact to add. If null, the version will be derived from the
 *                        manifest of the artifact
 * @param parentArtifacts the set of artifacts this artifact extends
 * @param additionalPlugins the set of plugins contained in the artifact that cannot be determined
 *                          through jar inspection. This set should include any classes that are plugins but could
 *                          not be annotated as such. For example, 3rd party classes like jdbc drivers fall into
 *                          this category.
 * @throws ArtifactAlreadyExistsException if the artifact already exists
 * @throws BadRequestException if the request is invalid. For example, if the artifact name or version is invalid
 * @throws ArtifactRangeNotFoundException if the parent artifacts do not exist
 * @throws IOException if a network error occurred
 * @throws UnauthenticatedException if the request is not authorized successfully in the gateway server
 */
public void add(NamespaceId namespace, String artifactName, ContentProvider<? extends InputStream> artifactContents, @Nullable String artifactVersion, @Nullable Set<ArtifactRange> parentArtifacts, @Nullable Set<PluginClass> additionalPlugins) throws ArtifactAlreadyExistsException, BadRequestException, IOException, UnauthenticatedException, ArtifactRangeNotFoundException, UnauthorizedException {
    URL url = config.resolveNamespacedURLV3(namespace, String.format("artifacts/%s", artifactName));
    HttpRequest.Builder requestBuilder = HttpRequest.post(url);
    if (artifactVersion != null) {
        requestBuilder.addHeader("Artifact-Version", artifactVersion);
    }
    if (parentArtifacts != null && !parentArtifacts.isEmpty()) {
        requestBuilder.addHeader("Artifact-Extends", Joiner.on('/').join(parentArtifacts));
    }
    if (additionalPlugins != null && !additionalPlugins.isEmpty()) {
        requestBuilder.addHeader("Artifact-Plugins", GSON.toJson(additionalPlugins));
    }
    HttpRequest request = requestBuilder.withBody(artifactContents).build();
    HttpResponse response = restClient.execute(request, config.getAccessToken(), HttpURLConnection.HTTP_CONFLICT, HttpURLConnection.HTTP_BAD_REQUEST, HttpURLConnection.HTTP_NOT_FOUND);
    int responseCode = response.getResponseCode();
    if (responseCode == HttpURLConnection.HTTP_CONFLICT) {
        throw new ArtifactAlreadyExistsException(response.getResponseBodyAsString());
    } else if (responseCode == HttpURLConnection.HTTP_BAD_REQUEST) {
        throw new BadRequestException(response.getResponseBodyAsString());
    } else if (responseCode == HttpURLConnection.HTTP_NOT_FOUND) {
        throw new ArtifactRangeNotFoundException(parentArtifacts);
    }
}
Also used : HttpRequest(io.cdap.common.http.HttpRequest) ArtifactRangeNotFoundException(io.cdap.cdap.common.ArtifactRangeNotFoundException) ArtifactAlreadyExistsException(io.cdap.cdap.common.ArtifactAlreadyExistsException) HttpResponse(io.cdap.common.http.HttpResponse) BadRequestException(io.cdap.cdap.common.BadRequestException) URL(java.net.URL)

Aggregations

ArtifactAlreadyExistsException (io.cdap.cdap.common.ArtifactAlreadyExistsException)12 IOException (java.io.IOException)10 ArtifactNotFoundException (io.cdap.cdap.common.ArtifactNotFoundException)8 BadRequestException (io.cdap.cdap.common.BadRequestException)6 NamespaceId (io.cdap.cdap.proto.id.NamespaceId)6 File (java.io.File)6 JsonSyntaxException (com.google.gson.JsonSyntaxException)4 ArtifactRange (io.cdap.cdap.api.artifact.ArtifactRange)4 InvalidArtifactRangeException (io.cdap.cdap.api.artifact.InvalidArtifactRangeException)4 AccessException (io.cdap.cdap.api.security.AccessException)4 ApplicationNotFoundException (io.cdap.cdap.common.ApplicationNotFoundException)4 ArtifactRangeNotFoundException (io.cdap.cdap.common.ArtifactRangeNotFoundException)4 InvalidArtifactException (io.cdap.cdap.common.InvalidArtifactException)4 NamespaceNotFoundException (io.cdap.cdap.common.NamespaceNotFoundException)4 NotFoundException (io.cdap.cdap.common.NotFoundException)4 AbstractBodyConsumer (io.cdap.cdap.common.http.AbstractBodyConsumer)4 Id (io.cdap.cdap.common.id.Id)4 WriteConflictException (io.cdap.cdap.internal.app.runtime.artifact.WriteConflictException)4 PluginNotExistsException (io.cdap.cdap.internal.app.runtime.plugin.PluginNotExistsException)4 CapabilityNotAvailableException (io.cdap.cdap.internal.capability.CapabilityNotAvailableException)4