You're viewing help content for version:


The Operations Dashboard in AEM 6 helps system operators to monitor AEM system health at a glance. It also provides auto-generated diagnosis informations on relevant aspects of AEM and allows to configure and run self-contained maintenance automation to reduce project operations and support cases significantly. The Operations Dashboard can be extended with custom health checks and maintenance tasks. Further, Operations Dashboard data can be accessed from external monitoring tools via JMX.

The Operations Dashboard:

  • Is a one-click system status to help operations departments gain efficiency
  • Provides system health overview in a single, centralized place
  • Reduces time to find, analyze and fix issues
  • Provides self-contained maintenance automation that helps reduce project operations costs significantly

It can be accessed by going to Tools - Operations - Health Reports from the AEM Welcome screen.


In order to be able to access the Operations Dashboard, the logged in user must be part of the "Operators" user group. For more info, see documentation on User, Group and Access Right Administration.

Health Reports

The Health Report system provides information on the health of an AEM instance through Sling Health Checks. This can be done via either OSGI, JMX or HTTP requests (via JSON). It offers measurements and threshold of certain configurable counters and in some cases, will offer information on how to resolve the issue.

It has several features, described below.

Health Checks

The Health Reports are a system of cards indicating a good or a bad health with regard to a specific product area. These cards are visualizations of the Sling Health Checks that aggregate information from JMX and other sources and expose processed information again as MBeans. The MBeans can be inspected also in the JMX web console, under the domain.

The Health Report interface can be accessed by going to Tools - Operations - Health Reports from the AEM Welcome screen, or directly by accessing the following URL:



The card system has three status states: OK, WARN and CRITICAL. The states are a result of rules and thresholds that can be configured through the user interface by hovering the mouse over the card and then clicking the gear icon in the action bar:


Health Check Types

There are two types of health checks in AEM 6:

  1. Individual Health Checks
  2. Composite Health Checks

An Individual Health Check is a single health check that corresponds to a status card. Individual Health Checks can be configured with rules or thresholds and they can provide one or more links to solve identified health issues. For example, if there are ERROR entries logged in the system, you will find them on the details page of the "Log Errors" along with a link to the "Log Message" analyzer in the Diagnosis Tools section so that you can actually inspect the errors.

A Composite Health Check is a check that aggregates information from several individual checks.

Composite health checks are configured with the aid of filter tags. In essence, all single checks that have the same filter tag will be grouped as a composite health check. A Composite Health Check will have an OK status only if all the single checks it aggregates have OK statuses as well.

How to create Health Checks

There are two types of Health Checks you can create for the Operations Dashboard: an individual health check and a composite health check.

Creating an individual Health Check

Creating an individual Health Check involves two steps: creating a Sling Health Check and adding an entry for the Health Check in the Dashboard's configuration nodes:

  1. In order to create a Sling Health Check, you need to create an OSGI component implementing the Sling Health Check interface. You will add this component inside a bundle. The properties of the component will fully identify the Health Check. Once the component is installed, a JMX Mbean will automatically be created for the Health Check. See the Sling Health Check Documentation for more information.
    Example of a Sling Health Check component:

    @Component(metatype=true, label="Example Health Check",
            description="This is an example Health Check.")
            @Property(name=HealthCheck.NAME, value="Example",
                    label="Name", description="Name of the health check."),
            @Property(name=HealthCheck.TAGS, unbounded= PropertyUnbounded.ARRAY, value={"example", "test"},
                    label="Tags", description="Tags for the health check."),
            @Property(name=HealthCheck.MBEAN_NAME, value="exampleHealthCheck",
                    label="MBean Name", description="Name of the JMX mbean to register for this check.")
    public class ExampleHealthCheck implements HealthCheck {


    The MBEAN_NAME property defines the name of the mbean that will be generated for this health check.

  2. After creating a Health Check, it is necessary to insert an entry for it in the Dashboard's configuration nodes, in order to make it accessible in the web interface. For this step, it is necessary to know the JMX Mbean name of the Health Check (the MBEAN_NAME property). To create a configuration for the Health Check, open CRX and add a new node (of type nt:unstructured) under this location: /apps/settings/granite/operations/hc

    The name of the node is the name of the health check that will appear in the Operations Dashboard. It should have the following properties:

    • Name: sling:resourceType 
      • Type: String
      • Value: granite/operations/components/mbean
    • Name: resource
      • Type: String
      • Value: /system/sling/monitoring/mbeans/org/apache/sling/healthcheck/HealthCheck/exampleHealthCheck


    The resource path above is created in the following way: if the mbean name of your Health Check is "test", add "test" to the end of this path: 


    So the final path will be:


Creating a Composite Health Check

A Composite Health Check's role is to aggregate a number of individual Health Checks sharing a set of common features. For instance, the Security Composite Health Check groups together all the individual health checks performing security checks. In order to create a composite, several steps are required that involve creating a Composite Health Check OSGI configuration and adding an entry for the Composite Health Check in the Dashboard's configuration nodes.


  1. Go to the Web Configuration Manager in the OSGI Console. You can do this by accessing http://serveraddress:port/system/console/configMgr

  2. Search for the entry called Apache Sling Composite Health Check. After you find it, notice that there are two configurations already available: one for the System Checks and another one for the Security Checks.

  3. Create a new configuration by pressing the "+" button on the right hand side of the configuration. A new window will appear, as shown below:

  4. Create a configuration and save it. An Mbean will be created with the new configuration.

    The purpose of each configuration property is as follows:

    • Name ( The name of the Composite Health Check. It can be anything, but a meaningful name is recommended.
    • Tags (hc.tags): The tags for this Health Check. If this composite health check is intended to be a part of another composite health check (such as in a hierarchy of health checks), add the tags this composite is related to.
    • MBean Name ( The name of the Mbean that will be given to the JMX MBean of this composite health check.
    • Filter Tags (filter.tags): This is a property specific to composite health checks. These are the tags which the composite should aggregate. The composite health check will aggregate under its group all the health checks that have any tag matching any of the filter tags of this composite. For example, a composite health check having the filter tags test and check will aggregate all the individual and composite health checks that have any of the test and check tags in their tags property (hc.tags).



    A new JMX Mbean is created for each new configuration of the Apache Sling Composite Health Check.

  5. Finally, the entry of the composite health check that has just been created needs to be added in the Operations Dashboard configuration nodes. The procedure for this is the same as with individual health checks: a node of type nt:unstructured needs to be created under /apps/settings/granite/operations/hc. The resource property of the node will be defined by the value of in the OSGI configuration.

    If, for example, you created a configuration and set the value to diskusage, the configuration nodes will look like this:

    • Name: Composite Health Check
      • Type: nt:unstructured

    With the following properties:

    • Name: sling:resourceType 
      • Type: String
      • Value: granite/operations/components/mbean
    • Name: resource
      • Type: String
      • Value: /system/sling/monitoring/mbeans/org/apache/sling/healthcheck/HealthCheck/diskusage



    If you create individual health checks that logically belong under a composite check that is already present in the Dashboard by default, they will be automatically captured and grouped under the respective composite check. Because of this, there is no need to create a new configuration node for those checks.

    For example, if you create a individual security health check, all you need to do assign it the "security" tag, and it is intalled, it will automatically appear under the Security Checks composite in the Operations Dashboard.


Health Checks Provided with AEM

Healthcheck Name Filter tags for Composite Health Checks Tags Description Default threshold (can be configured) Recommended actions
System Checks system   A health check is generated automatically for all maintenance tasks. This will indicate if the last run of the task was not successful or if it has not yet run at all.    
Security Checks security   The Security Checks composite groups together individual health checks related to security. The individual health checks are related to the security checklist available at the Security Checklist documentation page.    
CRXDE Support  
  • bundles
  • security
  • production
Health Check fails if the CRXDE Support bundle is active.    
Default Login Accounts  
  • login
  • security
  • production
Health Check fails if the default login accounts have not been disabled.    
Sling Get Servlet  
  • dos
  • sling
  • security
  • production
Health Check fails if the default Sling Get Servlet configuration is not following the security guidelines.    
CQ Dispatcher Configuration
  • dispatcher
  • production
  • security
Checks the basic configuration of the Dispatcher component.    
HTML Library Manager  
  • cq
  • security
  • production


Checks if the default HTML Library Manager configuration follows the security guidelines.    
Replication and Transport Users  
  • security
  • replication
  • cq
Health Check that checks the replication and transport users.    
Sling Java Script Handler  
  • sling
  • security
  • production
Checks if the Sling Java Script Handler configuration follows the security guidelines.    
Sling Jsp Script Handler  
  • sling
  • security
  • production
Checks if the Sling Jsp Script Handler configuration follows the security guidelines.    
Sling Referrer Filter  
  • sling
  • security
  • production
  • csrf
Checks if the Sling Referrer Filter is configured in order to prevent CSRF attacks    
User Profile Default Access  
  • acl
  • security
This health check checks if everyone has read access to user profiles.    
WCM Filter Config  
  • cq
  • security
  • production
Checks if the default WCM Filter configuration follows the security guidelines.    
WebDav Access  
  • bundles
  • security
  • production
This health check checks if the crxde-support bundle is active.    
Web Server Configuration  
  • webserver
  • production
  • security
  • clickjacking

This checks if the web server sends the X-FRAME-OPTIONS HTTP header set to SAMEORIGIN.

In order for it to function, this Health Check needs to be configured with the public server address that the dispatcher is configured with.

Replication Queue     Checks if replication is stuck. For example, if the first item of any replication queue has more retries than allowed by the configuration, then replication is considered to be stuck. numberOfRetriesAllowed=3
  • link to replication agents in order to inspect queues
  • link to log messages analyzer in the Diagnosis Tools section.
Log Errors     This health check notifies if there are errors in the log messages.    
Active Bundles     Health check that fails if there are inactive or unresolved bundles.    
Response Performance    

Health check responsible for requests performance. If a percentage of the requests are resolved above a critical or warning threshold value, the health check will be in a critical or a warning state. Otherwise, the health check is successful.

The requests considered are the ones made in the last time frame (by default, 60 minutes).

You can configure the requests percentage, the critical and warning thresholds in the configuration of the Requests Status Health Check.

The configuration for the time frame can be changed in the Adobe Granite Timed Requests Logger.

Query Performance    

Health check responsible for queries performance. If the average of the queries' resolve time from the last hour exceeds a critical or a warning threshold, the health check will be in a critical / warning state.

The critical and warning thresholds are configurable values, and they can be changed in the configuration of the Queries Health Check.

Warning Threshold = 10ms

Critical Threshold = 15ms

Production Ready Health Check
    This Health Check verifies if the Production Ready Package is installed. For more info on this package, please see below.
Disk Space    

This new Health Check checks for available disk space and will generate alerts if certain thresholds are exceeded. More specifically:

  • it returns a warning if the available disk space to repository size ratio is less than 10;
  • it returns a critical status if the available disk space to repository size ratio is less than 2.

These thresholds can be configured by pressing the configure button on the Disk Space Health Check card.

Warning Threshold = 10

Critical Threshold = 2

Code Cache Health Check     This is a Health Check that checks for several JVM conditions that can trigger a CodeCache bug present in Java 7. Fore more information about the bug, check this page.

Shows warnings if:

  • -XX:-UseCodeCacheFlushing is not set
  • XX:ReservedCodeCacheSize is less than 90 MB
Oservation Queue Lenght     A Health Check that monitors the number of events in the Oak observation Queue. If the number of events exceed 75% of the configured queue lenght, you will receive a warning. When the number exceeds the queue lenght, you will receive an error.
  • A warning for each EventListener or BackgroundObserver queue which is over 75% of the maxQueueSize, but under maxQueueSize.
  • An error for each queue whose length is over maxQueueSize

Monitoring with Nagios

The Health Check Dashboard can integrate with Nagios via the Granite JMX Mbeans. The below example illustrates how to add a check that shows used memory on the server running AEM.

  1. Setup and install Nagios on the monitoring server.

  2. Next, install the Nagios Remote Plugin Executor (NRPE).


    For more info on how to install Nagios and NRPE on your system, please consult the Nagios Documentation.

  3. Add a host definition for the AEM server. This can be done via the Nagios XI Web Interface, by using the Configuration Manager:

    1. Open a browser and point to the Nagios server.
    2. Press the Configure button in the top menu.
    3. In the left pane, press the Core Config Manager under Advanced Configuration.
    4. Press te Hosts link under the Monitoring section.
    5. Add the host definition:

    Below is an example of a host configuration file, in case you are using Nagios Core:

    define host {
       max_check_attempts 3
       check_period 24x7
       check-command check-host-alive
       contacts admin
       notification_interval 60
       notification_period 24x7
  4. Install Nagios and NRPE on the AEM server.

  5. Install the check_http_json plugin on both servers.

  6. Define a generic JSON check command on both servers:

    define command{
        command_name    check_http_json-int
        command_line    /usr/lib/nagios/plugins/check_http_json --user "$ARG1$" --pass "$ARG2$" -u 'http://$HOSTNAME$:$ARG3$/$ARG4$' -e '$ARG5$' -w '$ARG6$' -c '$ARG7$'
  7. Add a service for used memory on the AEM server:

    define service {
        use generic-service
        service_description AEM Author Used Memory
        check_command  check_http_json-int!<cq-user>!<cq-password>!<cq-port>!system/sling/monitoring/mbeans/java/lang/Memory.infinity.json!{noname}.mbean:attributes.HeapMemoryUsage.mbean:attributes.used.mbean:value!<warn-threshold-in-bytes>!<critical-threshold-in-bytes>
  8. Check your Nagios dashboard for the newly created service:


Diagnosis tools

The Operation Dashboard also provides access to Diagnosis Tools that can help finding and troubleshooting root causes of the warnings coming from the Health Check Dashboard, as well as providing important debug information for system operators.

Amongst its most important features are:

  • A log message analyzer
  • The ability to access heap and thread dumps
  • Requests and query performance analyzers

You can reach the Diagnosis Tools screen by going to Tools - Operations - Diagnosis from the AEM Welcome screen. You can also access the screen by directly accessing the following URL: http://serveraddress:port/libs/granite/operations/content/diagnosis.html


Log Messages

The log messages User Interface will display all ERROR messages by default. If you want to have more log messages displayed, you need to configure a logger with the appropriate log level.

The log messages use an in memory log appender and therefore, are not related to the log files. Another consequence is that changing the log levels in this UI will not change the information that gets logged in the traditional log files. Adding and removing loggers in this UI will only affect the in memory logger. Also, note that changing the logger configurations will be reflected in the future of the in memory logger - the entries that are already logged and are not relevant anymore are not deleted, but similar entries will not be logged in the future.

You can configure what gets logged by providing logger configurations from the upper left gear button in the UI. There, you can add, remove or update logger configurations. A logger configuration is composed of a log level (WARN / INFO / DEBUG) and a filter name. The filter name has the role of filtering the source of the log messages that get logged. Alternatively, if a logger should capture all the log messages for the specified level, the filter name should be "root". Setting the level of a logger will trigger the capture of all the messages with a level equal or higher than the one specified.


  • If you plan on capturing all the ERROR messages - no configuration is required. All the ERROR messages are captured by default.
  • If you plan on capturing all the ERROR, WARN and INFO messages - the logger name should be set to: "root", and the logger level to: INFO.
  • If you plan on capturing all the messages coming from a certain package (for example com.adobe.granite) - the logger name should be set to: "com.adobe.granite", and the logger level to: DEBUG (this will capture all the ERROR, WARN, INFO and DEBUG messages), as shown in the image below.


You can not set a logger name to capture only ERROR messages via a specified filter. By default, all the ERROR messages are captured.


The log messages user interface does not reflect the actual error log. Unless you are configuring other types of log messages in the UI, you will see ERROR messages only. For how to display specific log messages, see instructions above.


The settings in the diagnosis page do not influence what is logged to the log files and vice-versa. So, while the error log might catch INFO messages, you might not see them in the log messages UI. Also, through the UI it's possible to catch DEBUG messages from certain packages without it affecting the error log. For more information on how to configure the log files, see Logging.

Request performance

The Request Performance page allows the analysis of the slowest page requests processed. Only content requests will be registered on this page. More specifically, the following requests will be captured:

  1. Requests accessing resources under /content
  2. Requests accessing resources under /etc/design
  3. Requests having the ".html" extension

The page displays:

  • The time when the request was made
  • The URL and the method of request
  • The duration in milliseconds

By default, the slowest 20 page requests are captured, but the limit can be modified in the Configuration Manager.

In order to modify the number of slowest requests captured, you need to:

  1. Go to the Web Configuration Manager by accessing http://serveraddress:port/system/console/configMgr

  2. Look for an entry called Adobe Granite Timed Requests Logger.

  3. Click on the Edit button and modify the Longest requests history size property.

  4. Save the changes.

Query Performance

The Query Performance page allows the analysis of the slowest queries performed by the system. This information is provided by the repository in a JMX Mbean. In Jackrabbit, the com.adobe.granite.QueryStat JMX Mbean provides this information, while in the Oak repository, it is offered by org.apache.jackrabbit.oak.QueryStats.

The page displays:

  • The time when the query was made
  • The language of the query
  • The number of times the query was issued
  • The statement of the query
  • The duration in milliseconds

Explain Query

For any given query, Oak attempts to figure out the best way to execute based on the Oak indexes defined in the repository under the oak:index node. Depending on the query, different indexes may be chosen by Oak. Understanding how Oak is executing a query is the first step to optimizing the query.

The Explain Query is a tool that explains how Oak is executing a query. It can be accessed by going to Tools - Operations - Diagnosis from the AEM Welcome Screen, then clicking on Query Performance and switching over to the Explain Query tab.


  • Supports the Xpath, JCR-SQL and JCR-SQL2 query languages
  • Reports the actual execution time of the provided query
  • Detects slow queries and warns about queries that could be potentially slow
  • Reports the Oak index used to execute the query
  • Displays the actual Oak Query engine explanation
  • Provides click-to-load list of Slow and Popular queries

Once you are in the Explain Query UI, all you need to do in order to use it is enter the query and press the Explain button:


The first entry in the Query Explanation section is the actual explanation. The explanation will show the type of index that was used to execute the query.

The second entry is the execution plan.

Ticking the Include execution time box before running the query will also show the amount of time the query was executed in, allowing for more information that can be used for optimizing the indexes for your application or deployment.


The Index Manager

The purpose of the Index Manager is to facilitate index management such as maintaining indexes, viewing their status or triggering re-indexes.

It can be accessed by going to Tools - Operations - Diagnosis from the Welcome Screen, and then clicking the Index Manager button.

It can also be accessed directly at this URL: http://serveraddress:port/libs/granite/operations/content/diagnosis/tool.html/_granite_oakindexmanager


The UI can be used to filter indexes in the table by typing in the filter criteria in the search box in the upper left corner of the screen.

A single reindex can be triggered by clicking the icon in the Reindex column for the respective index. A bulk reindex can be triggered by selecting multiple indexes and clicking the Bulk Reindex button.


This will trigger the download of a zip containing useful information about the system status and configuration. The archive contains several text files containing information such as the active services, the log files and active configurations. This is particularly useful for taking a snapshot of the system status and send it for analysis.

Download Thread Dump

This will trigger the download of a zip containing information about the threads present in the system. Information about each thread is provided, such as its status, the classloader and the stacktrace.

Download Heap Dump

You also have the ability to download a snapshot of the heap, in order to analyze it at a later time. Take note that this will trigger the download of a large file, in the order of hundreds of megabytes.

Automated Maintenance Tasks

The Automated Maintenance Tasks page is a place where you can view and track recommended maintenance tasks scheduled for periodic execution. The tasks are integrated with the Health Check system and their execution has minimal impact on system performance. The tasks can also be manually executed from the interface.

In order to get to the Maintenance page in the Operations Dashboard, you need to go to Tools - Operations - Dashboard - Maintenance from the AEM Welcome screen, or directly follow this link:


By default, there are two automated maintenance tasks available in the Operations Dashboard:

  1. The Revision Clean Up, located under the Daily Maintenance Window menu
  2. The Workflow purge, located under the Weekly Maintenance Window menu.

The default timing for the daily maintenance window is 2 to 5 AM. The tasks configured to run in the weekly maintenance window will execute between 1 and 2 AM on Saturdays.

You can also configure the timings by pressing the gear icon on any of the two maintenance cards:



Since AEM 6.1, the existing maintenance windows can also be configured to run monthly.

Revision Clean Up

For more information on performing Revision Clean Up for AEM 6.3, see this dedicated article.

Workflow purge

Workflows can also be purged from the Maintenance Dashboard. In order to run the Workflow Purge task, you need to:

  1. Click on the Weekly Maintenance Window page.

  2. In the following page, click the Play button in the Workflow purge card.

Audit Log Maintenance

For Audit Log Maintenance, see the separate documentation page.

Custom Maintenance Tasks

Custom maintenance tasks can be implemented as OSGi services. As the maintenance task infrastructure is based on Apache Sling's job handling, a maintenance task must implement the java interface In addition, it must declare several service registration properties to be detected as a maintenance task, as listed below:

Service Property Name
Description Example
granite.maintenance.isStoppable Boolean attribute defining whether the task can be stopped by the user. If a task declares that it is stoppable it must check during its execution whether it has been stopped and then act accordingly. The default is false. true Optional
granite.maintenance.mandatory Boolean attribute defining whether a task is mandatory and must be run periodically. If a task is mandatory but currently not in any active schedule window, a Health Check will report this as an error. The default is false. true Optional A unique name for the task - this is used to reference the task. This is usally a simple name. MyMaintenanceTask Required
granite.maintenance.title A title displayed for this task My Special Maintenance Task Required
job.topics This is a unique topic of the maintenance task.
The Apache Sling job handling will start a job with exactly this topic to execute the maintenance task and as the task is registered for this topic it gets executed.
The topic must start with com/adobe/granite/maintenance/job/
com/adobe/granite/maintenance/job/MyMaintenanceTask Required

Apart from the above service properties, the process() method of the JobConsumer interface needs to be implemented by adding the code that should be executed for the maintance task. The provided JobExecutionContext can be used to output status information, check if the job is stopped by the user and create a result (success or failed).

For situations where a maintenance task should not be run on all installations (for example, run only on the publish instance), you can make the service require a configuration in order to be active by adding @Component(policy=ConfigurationPolicy.REQUIRE). You can then mark the according configuration as being run mode dependent in the repository. For more information, see Configuring OSGi.

Below is an example of a custom maintenance task that deletes files from a configurable temporary directory which have been modified in the last 24 hours:




 * #%L

 * sample-maintenance-task

 * %%

 * Copyright (C) 2014 Adobe

 * %%

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at




 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,


 * See the License for the specific language governing permissions and

 * limitations under the License.

 * #L%



package com.adobe.granite.samples.maintenance.impl;



import java.util.Calendar;

import java.util.Collection;

import java.util.Map;





import org.apache.felix.scr.annotations.Activate;

import org.apache.felix.scr.annotations.Component;

import org.apache.felix.scr.annotations.Properties;

import org.apache.felix.scr.annotations.Property;

import org.apache.felix.scr.annotations.Service;







import org.slf4j.Logger;

import org.slf4j.LoggerFactory;


import com.adobe.granite.maintenance.MaintenanceConstants;


@Component(metatype = true,

        label = "Delete Temp Files Maintenance Task",

        description = "Maintatence Task which deletes files from a configurable temporary directory which have been modified in the last 24 hours.")



        @Property(name = MaintenanceConstants.PROPERTY_TASK_NAME, value = "DeleteTempFilesTask", propertyPrivate = true),

        @Property(name = MaintenanceConstants.PROPERTY_TASK_TITLE, value = "Delete Temp Files", propertyPrivate = true),

        @Property(name = JobConsumer.PROPERTY_TOPICS, value = MaintenanceConstants.TASK_TOPIC_PREFIX

                + "DeleteTempFilesTask", propertyPrivate = true) })

public class DeleteTempFilesTask implements JobExecutor {


    private static final Logger log = LoggerFactory.getLogger(DeleteTempFilesTask.class);


    @Property(label = "Temporary Directory", description="Temporary Directory. Defaults to the system property.")

    private static final String PROP_TEMP_DIR = "temp.dir";


    private File tempDir;



    private void activate(Map<string, object=""> properties) {

        this.tempDir = new File(PropertiesUtil.toString(properties.get(PROP_TEMP_DIR),





    public JobExecutionResult process(Job job, JobExecutionContext context) {"Deleting old temp files from {}.", tempDir.getAbsolutePath());

        Collection<file> files = FileUtils.listFiles(tempDir, new LastModifiedBeforeYesterdayFilter(),


        int counter = 0;

        for (File file : files) {

            log.debug("Deleting file {}.", file.getAbsolutePath());



            // TODO - capture the output of delete() and do something useful with it


        return context.result().message(String.format("Deleted %s files.", counter)).succeeded();




     * IOFileFilter which filters out files which have been modified in the last 24 hours.



    private static class LastModifiedBeforeYesterdayFilter implements IOFileFilter {


        private final long minTime;


        private LastModifiedBeforeYesterdayFilter() {

            Calendar cal = Calendar.getInstance();

            cal.add(Calendar.DATE, -1);

            this.minTime = cal.getTimeInMillis();




        public boolean accept(File dir, String name) {

            // this method is never actually called.

            return false;




        public boolean accept(File file) {

            return file.lastModified() <= this.minTime;







experiencemanager-java-maintenancetask-sample src/main/java/com/adobe/granite/samples/maintenance/impl/

Once the service is deployed, it will be exposed to the Operations Dashboard UI and can be added to one of the available maintenance schedules:


This will add a corresponding resource at /apps/granite/operations/config/maintenance/[schedule]/[taskname]. If the task is run mode dependent, the property granite.operations.conditions.runmode needs to be set on that node with the values of the runmodes which need to be active for this maintenance task.

Maintenance Tasks Shipped with AEM

AEM 6 ships with a default set of automated maintenance tasks. Below is a table describing the maintenance tasks and their availability for each of the storage elements present in AEM 6.

Maintenance Task Name CRX2 Tar MK
Mongo MK Maintenance Window Remarks
Version Purge Yes Yes Yes Configurable. Not enabled by default. It can be added manually by pressing the "+" button in the "Weekly Maintenance Window" section of the Operations Dashboard.
Workflow Purge WorkflowPurgeTask Yes Yes Yes Weekly A purge configuration must first be created in order for the task to run successfully.
DataStore Garbage Collection
DataStoreGarbageCollectionTask Yes No Yes Weekly  
Tar Compaction/Optimization TarOptimizeTask Yes No No Daily  
Revision Clean Up
RevisionCleanupTask No Yes Yes Daily  
Tar Index Merge
TarIndexMergeTask Yes No No Daily  
AuditLog Mainenance Task Yes Yes Yes Weekly