Expand my Community achievements bar.

Join expert-led, customer-led sessions on Adobe Experience Manager Assets on August 20th at our Skill Exchange.
SOLVED

Scheduler doesn't run servlet

Avatar

Level 5

Hi, I have a servlet that performs a function, I have tested it and it works, to make it execute it is necessary to write the domain name followed by bin/serviceexecution.

But now we want it to run only every so often, for that we have created a scheduler.

The problem is that the scheduler is executed but when arriving to the call of the servlet, this fails, mentioning that we do not have permissions to execute the servlet.

I think it is because to execute this servlet I need permissions, which I don't know how to set them.

How could I add them or know what is the error.

this is the code I made


@Component(service = Runnable.class, immediate = true)
@Designate(ocd = SchedulerUseConfig.class)
public class SchedulerUse implements Runnable {

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

    @Reference
    private Scheduler scheduler;

    private int schedulerId;
    private SchedulerUseConfig config;

    @Activate
    @Modified
    protected void activate(SchedulerUseConfig config) {
        this.config = config;
        schedulerId = config.schedulerName().hashCode();
       
        log.info("SchedulerUse: Activating or Modifying");
        updateScheduler();
    }

    @Deactivate
    protected void deactivate() {
        log.info("SchedulerUse: Deactivating");
        removeScheduler();
    }

    private void removeScheduler() {
        log.info("SchedulerUse: Removing Scheduler Job '{}'", schedulerId);
        scheduler.unschedule(String.valueOf(schedulerId));
    }

    private void updateScheduler() {
        removeScheduler();
        if (config.enabled()) {
            log.info("SchedulerUse: Adding scheduler");
            ScheduleOptions scheduleOptions = scheduler.EXPR(config.cronExpression());
            scheduleOptions.name(String.valueOf(schedulerId));
            scheduleOptions.canRunConcurrently(false);
            scheduler.schedule(this, scheduleOptions);
            log.info("SchedulerUse: Cache Clean Scheduler added successfully with cron expression: {}", config.cronExpression());
        } else {
            log.info("SchedulerUse: Scheduler is disabled, no scheduler job created");
        }
    }

    @Override
    public void run() {
        log.info("SchedulerUse: Starting scheduled run");
        if (config.enabled()) {
            try {
                String url = config.baseUrl() + "/bin/serviceexecution";
                URL servletUrl = new URL(url);
                HttpURLConnection connection = (HttpURLConnection) servletUrl.openConnection();
                connection.setRequestMethod("GET");
                int responseCode = connection.getResponseCode();
                log.info("SchedulerUse: Response Code: {}", responseCode);
                if (responseCode != HttpURLConnection.HTTP_OK) {
                    log.error("SchedulerUse: Servlet call failed with response code: {}", responseCode);
                }
            } catch (IOException e) {
                log.error("Error: Error while calling servlet", e);
            }
        } else {
            log.info("Message: Scheduler is disabled, skipping run");
        }
        log.info("Message: Finished scheduled run");
    }
}
Topics

Topics help categorize Community content and increase your ability to discover relevant content.

1 Accepted Solution

Avatar

Correct answer by
Level 5

Hi @Aaron_Dempwolff ,

 

If you want to schedule the logic currently written inside your servlet, the better approach is to define an OSGi service that contains the actual business logic. Then, both your servlet and your scheduler can simply call this service directly instead of making an HTTP call.

 

The issue you're facing is happening because, when your scheduler makes an HTTP request to the servlet, it is treated as an anonymous user (no authentication), and therefore you don't have the permissions to execute the servlet.

Let me know if it works.

Thanks.

View solution in original post

6 Replies

Avatar

Correct answer by
Level 5

Hi @Aaron_Dempwolff ,

 

If you want to schedule the logic currently written inside your servlet, the better approach is to define an OSGi service that contains the actual business logic. Then, both your servlet and your scheduler can simply call this service directly instead of making an HTTP call.

 

The issue you're facing is happening because, when your scheduler makes an HTTP request to the servlet, it is treated as an anonymous user (no authentication), and therefore you don't have the permissions to execute the servlet.

Let me know if it works.

Thanks.

Avatar

Level 5

It works, thank you, even the cool part is that it work now like an scheduler and it works executing manually too.

Avatar

Community Advisor

Hi @Aaron_Dempwolff,

The exact problem in your approach is that the scheduler was making an HTTP call to the servlet endpoint (/bin/serviceexecution) internally.
Since the HTTP call originates from inside AEM without any authentication, it is treated as an anonymous user request.
Because of that, permissions are missing, and the servlet execution fails.

Instead of making an HTTP call from within the scheduler (which treats the request as anonymous and causes permission issues), IMO, the correct architectural approach is:

  • Extract the core business logic from the servlet into a separate OSGi service.

  • Inject and use this service both inside the servlet and the scheduler.

  • This way, the logic runs internally within the OSGi container, bypassing the need for HTTP requests and avoiding authentication or permission issues entirely.

  • It also improves maintainability, testability, and performance since no network overhead is involved.

This way it helps you to ensures that servlets handle HTTP communication only, while business logic is managed independently.

Hope that helps!


Santosh Sai

AEM BlogsLinkedIn


Avatar

Community Advisor

@Aaron_Dempwolff 

 

I second suggestions from @SantoshSai  and @ShivamKumar 

However, if for some reason you cannot change the code immediately, then consider using SlingRequestProcessor . Please refer the blog:

 

https://kiransg.com/2023/05/16/sling-servlet-helpers/


Aanchal Sikka

Avatar

Community Advisor

Hi @Aaron_Dempwolff ,

You're running into a common AEM mistake: your scheduler is calling your servlet via HTTP, which executes as an anonymous user — causing permission errors when accessing restricted content or services.
Step 1: Extract Business Logic to a Service

@Component(service = MyService.class)
public class MyService {

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

    public void executeLogic() {
        //  Put your servlet's logic here
        log.info("MyService: Logic executed");
    }
}

Step 2: Use This Service in Your Servlet

@Component(
    service = Servlet.class,
    property = {
        "sling.servlet.paths=/bin/serviceexecution",
        "sling.servlet.methods=GET"
    }
)
public class MyServlet extends SlingSafeMethodsServlet {

    @Reference
    private MyService myService;

    @Override
    protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException {
        myService.executeLogic(); //  Call the shared logic
        response.getWriter().write("Logic executed from servlet");
    }
}

Step 3: Use Same Service in Your Scheduler Update your scheduler:

@Component(service = Runnable.class, immediate = true)
@Designate(ocd = SchedulerUseConfig.class)
public class SchedulerUse implements Runnable {

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

    @Reference
    private Scheduler scheduler;

    @Reference
    private MyService myService; //  Inject business logic

    private int schedulerId;
    private SchedulerUseConfig config;

    @Activate
    @Modified
    protected void activate(SchedulerUseConfig config) {
        this.config = config;
        schedulerId = config.schedulerName().hashCode();
        updateScheduler();
    }

    @Deactivate
    protected void deactivate() {
        removeScheduler();
    }

    private void removeScheduler() {
        scheduler.unschedule(String.valueOf(schedulerId));
    }

    private void updateScheduler() {
        removeScheduler();
        if (config.enabled()) {
            ScheduleOptions scheduleOptions = scheduler.EXPR(config.cronExpression());
            scheduleOptions.name(String.valueOf(schedulerId));
            scheduleOptions.canRunConcurrently(false);
            scheduler.schedule(this, scheduleOptions);
        }
    }

    @Override
    public void run() {
        log.info("Scheduler: Triggering logic execution");
        myService.executeLogic(); //  Call shared logic (no HTTP)
    }
}

Benefits of This Approach:
- No authentication issues
- No unnecessary HTTP overhead
- Centralized logic
- Cleaner separation of concerns
- Works in Cloud Service, AMS, On-Prem setups

Regards,
Amit

Avatar

Community Advisor

@Aaron_Dempwolff Did you find the suggestions helpful? Please let us know if you require more information. Otherwise, please mark the answer as correct for posterity. If you've discovered a solution yourself, we would appreciate it if you could share it with the community. Thank you!


Aanchal Sikka