AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified Apache-2.0 Self-run

Asset Manager

skill-adobe-skills-asset-manager · by adobe

AEM Cloud Service expert skill for the Asset Manager API (com.day.cq.dam.api.AssetManager). Covers two migration paths — Path A (create/upload, replacing createAssetForBinary/getAssetForBinary/client-facing createAsset with Direct Binary Access via @adobe/aem-upload) and Path B (delete, replacing removeAssetForBinary with in-JVM resolver.delete() + commit or external HTTP Assets API with IMS bear…

No reviews yet
0 installs
26 views
0.0% view→install

Install

$ agentstack add skill-adobe-skills-asset-manager

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-adobe-skills-asset-manager)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Asset Manager? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Asset Manager API — AEM as a Cloud Service

Overview

com.day.cq.dam.api.AssetManager is not removed on AEM CS — but several of its operations are. The binary-path APIs that relied on direct filesystem access (createAssetForBinary, getAssetForBinary, removeAssetForBinary) do not exist on CS because the cloud runtime does not expose a filesystem path to the AEM JVM. Binary I/O for client uploads moves to Direct Binary Access — bytes flow directly between the client and Adobe's binary store, bypassing the JVM entirely.

API status on AEMaaCS:

| API | Status | What to use instead | |-----|--------|---------------------| | AssetManager.getAsset(path) | ✅ Supported | No change — still works for reads | | AssetManager.createAsset(path, InputStream, mimeType, doSave) | ⚠️ Strongly discouraged for client-facing uploads (2 GB binary limit, blocks the JVM, asset-processing pipeline expects Direct Binary Access). Still callable for in-JVM utilities where the binary is small and already in the JVM (bundled resources, fixtures, back-office imports). | Direct Binary Access for client uploads (@adobe/aem-upload JavaScript SDK). In-JVM small-file creation OK. | | AssetManager.createAssetForBinary(binaryFilePath, doSave) | ❌ Removed — relied on filesystem path the cloud runtime does not expose | Direct Binary Access | | AssetManager.getAssetForBinary(binaryFilePath) | ❌ Removed | resolver.getResource(repoPath).adaptTo(Asset.class) — look up by repository path, not binary path | | AssetManager.removeAssetForBinary(binaryFilePath, doSave) | ❌ Removed | In-JVM: resolver.delete(resource) + resolver.commit(). External: HTTP Assets API DELETE /api/assets{path} |

Three CS-specific principles:

| Principle | Why | |-----------|-----| | Binary I/O for client uploads goes Direct Binary Access — never through the JVM | The cloud runtime does not have a JVM-accessible filesystem; bytes through the JVM are slow, memory-bound, and blocked by a 2 GB ceiling | | In-JVM delete uses resolver.delete() + resolver.commit() with a service user — never AEM calling its own HTTP API | Self-looping HTTP adds latency, requires credentials you shouldn't store, and breaks idempotency | | External callers authenticate with IMS / dev-console bearer tokens — never hardcoded passwords or userId:password strings | AEMaaCS has no admin user with a static password; IMS service credentials are the only supported external auth path |


Classification — choose before making any changes

Uses AssetManager.createAssetForBinary(...) or AssetManager.getAssetForBinary(...) (removed APIs) → Apply Path A (C1–C3).

Uses AssetManager.createAsset(path, InputStream, mimeType, doSave) in a client-facing servlet (accepts upload from a browser or external caller) → Apply Path A — migrate to Direct Binary Access (C2 client-facing branch).

Uses AssetManager.removeAssetForBinary(...) (removed API) → Apply Path B (D1–D3).

Uses both a removed create API AND a removed delete API → Apply Path A first, then Path B. Both transformations are independent.

Uses AssetManager.getAsset(path) only (read access) → Already compliant — no migration needed.

Uses AssetManager.createAsset(path, InputStream, mimeType, doSave) in an in-JVM back-office utility (scheduled import, test fixture, asset post-processing — binary is already in the JVM and small) → Already supported — verify the surrounding code uses a service-user resolver and closes the stream in try-with-resources, then skip.

One pattern per session. If the file has multiple flows, migrate one direction (create OR delete) at a time.

Before starting: Read [../references/aem-cloud-service-pattern-prerequisites.md](../references/aem-cloud-service-pattern-prerequisites.md) and apply SCR→DS, service-user, and SLF4J fixes if present in the same changeset.


Complete example — Path A (create / upload)

Before (client-facing upload via AssetManager.createAsset)

package com.example.servlets;

import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import com.day.cq.dam.api.Asset;
import com.day.cq.dam.api.AssetManager;

import javax.servlet.ServletException;
import java.io.IOException;
import java.io.InputStream;

@Component(immediate = true, metatype = false)
public class CreateAssetServlet extends SlingAllMethodsServlet {

    @Reference
    private AssetManager assetManager;

    @Override
    protected void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response)
            throws ServletException, IOException {

        String assetPath = request.getParameter("path");
        String mimeType = request.getParameter("mimeType");
        InputStream inputStream = request.getInputStream();

        try {
            Asset asset = assetManager.createAsset(assetPath, inputStream, mimeType, true);
            response.setContentType("text/plain");
            response.getWriter().write("Asset created: " + asset.getPath());
        } catch (Exception e) {
            System.err.println("Error creating asset: " + e.getMessage());
            e.printStackTrace();
            response.setStatus(500);
            response.getWriter().write("Error: " + e.getMessage());
        }
    }
}

After — Cloud Service compatible (client-side Direct Binary Access)

On AEMaaCS the upload happens directly between the client and the binary store; the AEM JVM orchestrates the upload but does not carry the bytes. Delete the upload-accepting servlet entirely, and replace it with a client-side call to the Direct Binary Access HTTP API via @adobe/aem-upload:

import DirectBinary from '@adobe/aem-upload';

/**
 * Uploads a file to AEMaaCS via Direct Binary Access.
 * @param {File|Blob} file     The binary to upload
 * @param {string} assetPath   Repository destination, e.g. "/content/dam/my-site/file.pdf"
 * @param {string} host        AEM author host, e.g. "https://author-p123-e456.adobeaemcloud.com"
 * @param {string} token       IMS bearer token (Adobe Developer Console service credentials,
 *                             or short-lived user token from the AEM login flow)
 */
async function uploadAsset(file, assetPath, host, token) {
    const upload = new DirectBinary.DirectBinaryUpload();
    const options = new DirectBinary.DirectBinaryUploadOptions()
        .withUrl(`${host}/api/assets${assetPath}`)
        .withUploadFiles([{
            fileName: file.name,
            blob: file,
            fileSize: file.size
        }])
        .withHttpOptions({
            headers: {
                Authorization: `Bearer ${token}`   // IMS / dev-console token — never a static password
            }
        });

    return upload.uploadFiles(options);
}

If the servlet path must remain (routing, ACL, audit reasons), convert it to return 410 Gone with a documented replacement — do not silently call a removed API:

package com.example.servlets;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.ServletResolverConstants;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.Servlet;
import javax.servlet.ServletException;
import java.io.IOException;

@Component(service = Servlet.class, property = {
        ServletResolverConstants.SLING_SERVLET_PATHS + "=/bin/createasset"
})
public class CreateAssetServlet extends SlingAllMethodsServlet {

    private static final Logger LOG = LoggerFactory.getLogger(CreateAssetServlet.class);

    @Override
    protected void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response)
            throws ServletException, IOException {
        LOG.warn("Legacy upload endpoint hit — clients must use Direct Binary Access at /api/assets");
        response.setStatus(410);   // 410 Gone — the operation moved
        response.setContentType("application/json");
        response.getWriter().write(
                "{\"error\":\"Use Direct Binary Access (POST /api/assets + @adobe/aem-upload) to upload assets.\"}");
    }
}

Complete example — Path B (delete)

Before (removed removeAssetForBinary API)

package com.example.servlets;

import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import com.day.cq.dam.api.AssetManager;

import javax.servlet.ServletException;
import java.io.IOException;

@Component(immediate = true, metatype = false)
public class DeleteAssetServlet extends SlingAllMethodsServlet {

    @Reference
    private AssetManager assetManager;

    @Override
    protected void doDelete(SlingHttpServletRequest request, SlingHttpServletResponse response)
            throws ServletException, IOException {

        String binaryFilePath = request.getParameter("path");

        try {
            boolean isDeleted = assetManager.removeAssetForBinary(binaryFilePath, true);
            response.setContentType("text/plain");
            if (isDeleted) {
                response.getWriter().write("Asset deleted: " + binaryFilePath);
            } else {
                response.setStatus(404);
                response.getWriter().write("Asset not found: " + binaryFilePath);
            }
        } catch (Exception e) {
            System.err.println("Error deleting asset: " + e.getMessage());
            e.printStackTrace();
            response.setStatus(500);
            response.getWriter().write("Error: " + e.getMessage());
        }
    }
}

After — in-JVM delete with a service-user resolver (recommended for code already inside AEM)

Server-side code that already has a trusted ResourceResolver should delete assets directly via the resource API. No HTTP, no credentials, no self-loopback through AEM's own API:

package com.example.servlets;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.PersistenceException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.api.servlets.ServletResolverConstants;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.Servlet;
import javax.servlet.ServletException;
import java.io.IOException;
import java.util.Collections;

@Component(service = Servlet.class, property = {
        ServletResolverConstants.SLING_SERVLET_PATHS + "=/bin/deleteasset"
})
public class DeleteAssetServlet extends SlingAllMethodsServlet {

    private static final Logger LOG = LoggerFactory.getLogger(DeleteAssetServlet.class);
    private static final String SUBSERVICE = "asset-admin-service";

    @Reference
    private ResourceResolverFactory resolverFactory;

    @Override
    protected void doDelete(SlingHttpServletRequest request, SlingHttpServletResponse response)
            throws ServletException, IOException {

        String assetPath = request.getParameter("path");
        if (assetPath == null || assetPath.isEmpty() || !assetPath.startsWith("/content/dam/")) {
            response.setStatus(400);
            response.setContentType("application/json");
            response.getWriter().write("{\"error\":\"valid /content/dam path parameter required\"}");
            return;
        }

        try (ResourceResolver resolver = resolverFactory.getServiceResourceResolver(
                Collections.singletonMap(ResourceResolverFactory.SUBSERVICE, SUBSERVICE))) {

            Resource resource = resolver.getResource(assetPath);
            if (resource == null) {
                response.setStatus(404);
                response.setContentType("application/json");
                response.getWriter().write("{\"error\":\"asset not found\"}");
                return;
            }

            resolver.delete(resource);
            resolver.commit();

            response.setContentType("application/json");
            response.getWriter().write("{\"success\":true}");
            LOG.info("Deleted asset at {}", assetPath);

        } catch (LoginException e) {
            LOG.error("Could not open service resolver for subservice '{}'", SUBSERVICE, e);
            response.setStatus(500);
            response.setContentType("application/json");
            response.getWriter().write("{\"error\":\"internal error\"}");
        } catch (PersistenceException e) {
            LOG.error("Commit failed while deleting asset {}", assetPath, e);
            response.setStatus(500);
            response.setContentType("application/json");
            response.getWriter().write("{\"error\":\"internal error\"}");
        }
    }
}

After — external-caller delete via the HTTP Assets API

If the deleter is outside the AEM JVM (integration backend, CLI, worker), call the HTTP API with an IMS or dev-console bearer token — never a hardcoded password:

import axios from 'axios';

/**
 * Deletes an asset from AEMaaCS via the HTTP Assets API.
 * Caller must provide a bearer token from Adobe Developer Console service credentials
 * or a short-lived user token from the AEM login flow.
 */
export async function deleteAsset({ host, assetPath, bearerToken }) {
    const response = await axios.delete(`${host}/api/assets${assetPath}`, {
        headers: { Authorization: `Bearer ${bearerToken}` },
        validateStatus: status => status === 200 || status === 204 || status === 404
    });
    return response.status !== 404;
}

C1 — Replace createAssetForBinary / getAssetForBinary

These APIs do not exist on AEMaaCS. Remove every call.

// BEFORE (removed APIs)
assetManager.createAssetForBinary(binaryFilePath, doSave);
Asset asset = assetManager.getAssetForBinary(binaryFilePath);

Replacements:

| Legacy call | AEMaaCS replacement | |-------------|---------------------| | createAssetForBinary(binaryFilePath, doSave) | Direct Binary Access via @adobe/aem-upload or HTTP POST /api/assets. The binary never sits on the AEM filesystem. | | getAssetForBinary(binaryFilePath) | resolver.getResource(repoPath).adaptTo(Asset.class) using the repository path (not a filesystem path) |

// Direct Binary Access — client side
const DirectBinary = require('@adobe/aem-upload');
const upload = new DirectBinary.DirectBinaryUpload();
const options = new DirectBinary.DirectBinaryUploadOptions()
    .withUrl(`${host}/api/assets${assetPath}`)
    .withUploadFiles(uploadFiles)
    .withHttpOptions({ headers: { Authorization: `Bearer ${token}` } });
await upload.uploadFiles(options);
// Look up by repository path — replaces getAssetForBinary
Resource resource = resolver.getResource("/content/dam/my-site/report.pdf");
Asset asset = resource != null ? resource.adaptTo(Asset.class) : null;

The legacy "binary path" concept (a filesystem path under the AEM install) does not exist on AEMaaCS. Anywhere your legacy code passed a binary path, replace with the asset's repository path (/content/dam/...).


C2 — Decide whether `createAss

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

  • Author: adobe
  • Source: adobe/skills
  • License: Apache-2.0
  • Homepage: https://www.adobe.com/ai/overview.html

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.