Hi everyone,
I’ve implemented a simple Sling scheduled job in Cloud Service. It works perfectly in my local SDK, but once deployed to the dev environment, the job never seems to run - and there are no related logs or errors.
Below the core code:
@component(
service = Runnable.class,
immediate = true,
configurationPolicy = ConfigurationPolicy.REQUIRE
)
@Designate(ocd = MyScheduledTask.Config)
public class MyScheduledTask implements Runnable {
@ObjectClassDefinition(name = "My Scheduled Task Configuration")
public @interface Config {
@AttributeDefinition(name = "CRON Expression")
String scheduler_expression() default "0 */1 * * * ?"; // Every 1 minute
@AttributeDefinition(name = "Concurrent execution")
boolean scheduler_concurrent() default false;
}
private static final Logger LOG = LoggerFactory.getLogger(MyScheduledTask.class);
@activate
@MODIFIED
protected void activate(Config config) {
LOG.info("Scheduled Task configured with expression: {}", config.scheduler_expression());
}
@Override
public void run() {
LOG.info("MyScheduledTask is running...");
}
}
And in ui.config/config.dev, I have configured:
{
"scheduler.expression": "0 */1 * * * ?",
"scheduler.concurrent": false
}
In the dev environment, the job never executes and I don’t see any logs or errors in the Cloud Manager logs, making it hard to debug.
Is there a specific configuration or restriction that prevents scheduled jobs from running?
Thanks in advance!
Solved! Go to Solution.
Views
Replies
Total Likes
Hi @SanaQu,
Based on what you described and some common pitfalls we have seen, here are a few key things to double-check in your setup:
1. Scheduled jobs must be enabled via OSGi configurations
Make sure the scheduler.expression and other values are configured correctly for the environment (like dev) via ui.config under the correct runmode folder.
For example, in:
ui.config/src/dev/com/example/core/MyScheduledTask.cfg.json
Make sure it's named exactly using the fully qualified class path, such as:
{
"scheduler.expression": "0 */1 * * * ?",
"scheduler.concurrent": false
}
Also ensure this configuration is part of your Cloud Manager deployment.
2. Use @Component(immediate = true) with caution
In AEM as a Cloud Service, immediate = true can sometimes prevent the service from being registered properly if dependencies are not satisfied at activation. Consider using immediate = false and control job behaviour via lifecycle management.
3. Logs might not show unless the job actually triggers
Logging level in AEM as a Cloud Service may suppress non-error logs unless explicitly configured. Use:
LOG.error("Task is running..."); // temporarily for visibility
Or configure custom loggers in ui.config to increase visibility.
4. Cloud readiness - avoid State-Based jobs
AEM as a Cloud Service is stateless and distributed, so Adobe recommends using Sling Jobs (org.apache.sling.event.jobs) or Adobe Scheduler Service via Cloud Manager for time-based automation, especially if reliability and scaling are concerns.
Sling Scheduler vs Sling Jobs – Adobe Best Practices
5. Deployment timing and bundle activation
Sometimes, if your bundle is active before the OSGi configuration is deployed, the @Activate method won't get the right values. To fix:
Ensure the .cfg.json file is deployed as part of the same package as your bundle
Or deploy configs before the bundle if separated
Quick debug tip
Add this to verify if your job is at least active:
curl -u admin:admin http://localhost:4502/system/console/status-slingscheduler
In Cloud Service, you won’t have this access, so rely on log statements or Adobe’s Developer Console for deeper telemetry.
Try this:
MyScheduledTask.java:
@Component(service = Runnable.class, configurationPolicy = ConfigurationPolicy.REQUIRE, property = {
"scheduler.concurrent=false"
})
public class MyScheduledTask implements Runnable {
private static final Logger LOG = LoggerFactory.getLogger(MyScheduledTask.class);
@Override
public void run() {
LOG.error("Scheduled Task is running..."); // Use ERROR level for Cloud visibility
}
}
MyScheduledTask.cfg.json:
{
"scheduler.expression": "0 */1 * * * ?",
"scheduler.concurrent": false
}
Place this under:
ui.config/src/dev/com/example/core/MyScheduledTask.cfg.json
I would not recommend using immediate=true, if dependencies are not met immediately, this can prevent activation. Best practice is to remove immediate=true and instead rely on lifecycle management or OSGi configuration.
Hi @SanaQu,
Based on what you described and some common pitfalls we have seen, here are a few key things to double-check in your setup:
1. Scheduled jobs must be enabled via OSGi configurations
Make sure the scheduler.expression and other values are configured correctly for the environment (like dev) via ui.config under the correct runmode folder.
For example, in:
ui.config/src/dev/com/example/core/MyScheduledTask.cfg.json
Make sure it's named exactly using the fully qualified class path, such as:
{
"scheduler.expression": "0 */1 * * * ?",
"scheduler.concurrent": false
}
Also ensure this configuration is part of your Cloud Manager deployment.
2. Use @Component(immediate = true) with caution
In AEM as a Cloud Service, immediate = true can sometimes prevent the service from being registered properly if dependencies are not satisfied at activation. Consider using immediate = false and control job behaviour via lifecycle management.
3. Logs might not show unless the job actually triggers
Logging level in AEM as a Cloud Service may suppress non-error logs unless explicitly configured. Use:
LOG.error("Task is running..."); // temporarily for visibility
Or configure custom loggers in ui.config to increase visibility.
4. Cloud readiness - avoid State-Based jobs
AEM as a Cloud Service is stateless and distributed, so Adobe recommends using Sling Jobs (org.apache.sling.event.jobs) or Adobe Scheduler Service via Cloud Manager for time-based automation, especially if reliability and scaling are concerns.
Sling Scheduler vs Sling Jobs – Adobe Best Practices
5. Deployment timing and bundle activation
Sometimes, if your bundle is active before the OSGi configuration is deployed, the @Activate method won't get the right values. To fix:
Ensure the .cfg.json file is deployed as part of the same package as your bundle
Or deploy configs before the bundle if separated
Quick debug tip
Add this to verify if your job is at least active:
curl -u admin:admin http://localhost:4502/system/console/status-slingscheduler
In Cloud Service, you won’t have this access, so rely on log statements or Adobe’s Developer Console for deeper telemetry.
Try this:
MyScheduledTask.java:
@Component(service = Runnable.class, configurationPolicy = ConfigurationPolicy.REQUIRE, property = {
"scheduler.concurrent=false"
})
public class MyScheduledTask implements Runnable {
private static final Logger LOG = LoggerFactory.getLogger(MyScheduledTask.class);
@Override
public void run() {
LOG.error("Scheduled Task is running..."); // Use ERROR level for Cloud visibility
}
}
MyScheduledTask.cfg.json:
{
"scheduler.expression": "0 */1 * * * ?",
"scheduler.concurrent": false
}
Place this under:
ui.config/src/dev/com/example/core/MyScheduledTask.cfg.json
I would not recommend using immediate=true, if dependencies are not met immediately, this can prevent activation. Best practice is to remove immediate=true and instead rely on lifecycle management or OSGi configuration.
Hi @SanaQu ,
Root Causes of Job Not Running in Cloud:
- immediate = true is risky in AEMaaCS — it may prevent service activation silently if dependencies are not ready.
- No scheduler.expression in @Component properties — Cloud needs static properties present at component level for proper binding.
- Logs at INFO level are filtered in AEM Cloud unless explicitly enabled — use ERROR or configure a custom logger.
- Config file naming/path issue — must match the fully qualified class name exactly.
Updated Java Code – AEMaaCS-Compatible
package com.example.core.schedulers;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.metatype.annotations.Designate;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.component.annotations.ConfigurationPolicy;
import org.osgi.framework.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Component(
service = Runnable.class,
configurationPolicy = ConfigurationPolicy.REQUIRE,
property = {
Constants.SERVICE_DESCRIPTION + "=My Scheduled Task",
"scheduler.concurrent=false"
// Do NOT hardcode CRON here; we’ll use cfg.json
}
)
@Designate(ocd = MyScheduledTask.Config)
public class MyScheduledTask implements Runnable {
private static final Logger LOG = LoggerFactory.getLogger(MyScheduledTask.class);
@ObjectClassDefinition(name = "My Scheduled Task Configuration")
public @interface Config {
@AttributeDefinition(name = "CRON Expression")
String scheduler_expression() default "0 */1 * * * ?";
@AttributeDefinition(name = "Concurrent Execution")
boolean scheduler_concurrent() default false;
}
@Activate
protected void activate(Config config) {
LOG.error("MyScheduledTask activated with CRON: {}", config.scheduler_expression());
}
@Override
public void run() {
LOG.error("MyScheduledTask is running in AEM Cloud!");
}
}
Configuration File
Path:
ui.config/src/main/content/jcr_root/apps/YOUR_PROJECT/osgiconfig/config.dev/com.example.core.schedulers.MyScheduledTask.cfg.json
Content:
{
"scheduler.expression": "0 */1 * * * ?",
"scheduler.concurrent": false
}
Regards,
Amit
Views
Replies
Total Likes
Views
Likes
Replies