Expand my Community achievements bar.

Guidelines for the Responsible Use of Generative AI in the Experience Cloud Community.
SOLVED

Trouble setting up a sling job on AEM 6.5. What am I missing?

Avatar

Level 2

I'm trying to create a Sling job and not having much luck. I've followed the docs on the Apache and Adobe sites but if keeps throwing a null pointer exception. I figure I've missed an important step that wasn't in the docs I read. Also, everything I've read thats AEM related is based on a watcher seeing a change to the JCR or a scheduled task that runs at a set time. We want to trigger it without a JCR update.

 

My goal is to have a user perform an action that ends with a job being created and immediately ran. This would mean a servlet or API is hit and it adds the job and triggers it to run immediately. I've written code to do this but it throws the NPE on the jobManager.addJob() method call.

 

This is the Apache example code that I've based my code on.

Create the job

import org.apache.sling.jobs.JobManager;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import java.util.Map;
import java.util.HashMap;

@Component
public class MyComponent {

    @reference
    private JobManager jobManager;
    
    public void startJob() {
        final Map<String, Object> props = new HashMap<String, Object>();
        props.put("item1", "/something");
        props.put("count", 5);
        
        jobManager.addJob("my/special/jobtopic", props);
    }        
}

 

Job consumer

import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.event.jobs.Job;
import org.apache.sling.event.jobs.consumer.JobConsumer;

@Component
@Service(value={JobConsumer.class})
@Property(name=JobConsumer.PROPERTY_TOPICS, value="my/special/jobtopic",)
public class MyJobConsumer implements JobConsumer {

    public JobResult process(final Job job) {
        // process the job and return the result
        return JobResult.OK;
    }
}

What I've been basing my work on.
Apache docs https://sling.apache.org/documentation/bundles/apache-sling-eventing-and-job-handling.html
Adobe https://experienceleague.adobe.com/docs/experience-manager-cloud-service/operations/asynchronous-job...
I've also exhausted my search skills on this topic.

Topics

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

6.5
1 Accepted Solution

Avatar

Correct answer by
Community Advisor

Hi @Art_Bird,

I suggest to try using the below as is separately (as two Java classes) and see if it works. If yes, then you can add it to your actual business logic. 

 

JobComponent.java (OSGi component that adds Job on activation - addJob on Activate method)

As we are adding the Job on activation, you should be able to see the LOG messages on installing/successful activation of your bundle. (Change the log level you are using accordingly - Debug/Info whichever it is set to.)

package com.aem.demoproject.core.listeners;

import org.apache.sling.event.jobs.Job;
import org.apache.sling.event.jobs.JobManager;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;

@Component
public class JobComponent {

    @Reference
    private JobManager jobManager;

    public static final String JOB_TOPIC="com/sling/eventing/page/job";
    private final Logger LOG = LoggerFactory.getLogger(this.getClass());

    @Activate
    protected void activate(){
        addJob();
    }

    public void addJob(){
        LOG.debug("Inside add job");
        Map<String,Object> param = new HashMap<String,Object>();
        param.put("eventPath","/content/demo");
        Job job = jobManager.addJob(JOB_TOPIC,param);
        LOG.info("Job "+job.getTopic()+" is created successfully on "+job.getCreated().getTime());

    }
}

JobConsumerImpl.java:

package com.aem.demoproject.core.listeners;

import org.apache.sling.event.jobs.Job;
import org.apache.sling.event.jobs.consumer.JobConsumer;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component(service=JobConsumer.class, immediate = true, property = {JobConsumer.PROPERTY_TOPICS + "=" + JobComponent.JOB_TOPIC})
public class JobConsumerImpl implements JobConsumer {

    private final Logger LOG = LoggerFactory.getLogger(this.getClass());

    @Override
    public JobResult process(Job job) {
        LOG.info("Inside job process method");
        String eventPath = (String) job.getProperty("eventPath");
        LOG.info("Event path={}", eventPath);
        return JobResult.OK; // Return based on actual implementation logic.
    }
}

 

View solution in original post

10 Replies

Avatar

Community Advisor

Hi @Art_Bird,

Could you please cross check the OSGi component (where you are adding the job) is active and the JobManager Reference is satisfied and bound to Service implementation.

Vijayalakshmi_S_0-1624915003769.png

 

Avatar

Community Advisor

Hi @Art_Bird,

 

Can you please try to add immediate=true with your components, as this will immediately activate your components.

@Component(immediate = true)
 
Thanks,
Chitra

Avatar

Community Advisor

Hi @Art_Bird 

 JobManager is normally from org.apache.sling.event.jobs.JobManager  package not from "org.apache.sling.jobs.JobManager" . you able to compile this with package?

 

Thanks

Dipti

 

Avatar

Level 2

I've updated the code I'm using based on what you all have suggested. Still getting the nullPointerException when jobManager.addJob(TOPIC, props) is ran.

@shelly-goelI made the adjustments to use R6 and switched to @OSGiService

@Dipti_ChauhanPackages are updated.

@ChitraMadanUsing immediate=true.

@Vijayalakshmi_SNot seeing JobManager referenced in the component.

 

import org.apache.sling.event.jobs.Job;
import org.apache.sling.event.jobs.JobManager;
import org.osgi.service.component.annotations.Component;
import org.apache.sling.models.annotations.injectorspecific.OSGiService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.HashMap;

@Component
public class TestStartJob {

	private static final Logger LOGGER = LoggerFactory.getLogger(TestStartJob.class);

	@OSGiService
	private JobManager jobManager;
	private static final String TOPIC = "my/special/jobtopic";

	public void startJob() {
		try {
			final Map<String, Object> props = new HashMap<String, Object>();
			props.put("foo", "bar");

			Job job = jobManager.addJob(TOPIC, props);
			String jobTopic = job.getTopic();
			LOGGER.debug(jobTopic);

		} catch (Exception e){
			LOGGER.debug(e.getMessage());
		}

	}
}

 

import org.apache.sling.event.jobs.Job;
import org.apache.sling.event.jobs.consumer.JobConsumer;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component(
		service = JobConsumer.class,
		property = {
				JobConsumer.PROPERTY_TOPICS + "=my/special/jobtopic"
		},
		// One of the few cases where immediate = true; this is so the Event Listener starts listening immediately
		immediate = true
)
public class TestJobConsumer implements JobConsumer {
	private static final Logger log = LoggerFactory.getLogger(TestJobConsumer.class);

	@Override
	public JobResult process(final Job job) {

		job.getProperty("foo");

		return JobConsumer.JobResult.OK;
	}
}

 

 

Avatar

Employee Advisor
@Art_Bird - Try with this package as well org.apache.sling.jobs.JobManager; The sling documentation mentions this pacakge: https://sling.apache.org/documentation/bundles/apache-sling-eventing-and-job-handling.html#jobs-guar...

Avatar

Correct answer by
Community Advisor

Hi @Art_Bird,

I suggest to try using the below as is separately (as two Java classes) and see if it works. If yes, then you can add it to your actual business logic. 

 

JobComponent.java (OSGi component that adds Job on activation - addJob on Activate method)

As we are adding the Job on activation, you should be able to see the LOG messages on installing/successful activation of your bundle. (Change the log level you are using accordingly - Debug/Info whichever it is set to.)

package com.aem.demoproject.core.listeners;

import org.apache.sling.event.jobs.Job;
import org.apache.sling.event.jobs.JobManager;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;

@Component
public class JobComponent {

    @Reference
    private JobManager jobManager;

    public static final String JOB_TOPIC="com/sling/eventing/page/job";
    private final Logger LOG = LoggerFactory.getLogger(this.getClass());

    @Activate
    protected void activate(){
        addJob();
    }

    public void addJob(){
        LOG.debug("Inside add job");
        Map<String,Object> param = new HashMap<String,Object>();
        param.put("eventPath","/content/demo");
        Job job = jobManager.addJob(JOB_TOPIC,param);
        LOG.info("Job "+job.getTopic()+" is created successfully on "+job.getCreated().getTime());

    }
}

JobConsumerImpl.java:

package com.aem.demoproject.core.listeners;

import org.apache.sling.event.jobs.Job;
import org.apache.sling.event.jobs.consumer.JobConsumer;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component(service=JobConsumer.class, immediate = true, property = {JobConsumer.PROPERTY_TOPICS + "=" + JobComponent.JOB_TOPIC})
public class JobConsumerImpl implements JobConsumer {

    private final Logger LOG = LoggerFactory.getLogger(this.getClass());

    @Override
    public JobResult process(Job job) {
        LOG.info("Inside job process method");
        String eventPath = (String) job.getProperty("eventPath");
        LOG.info("Event path={}", eventPath);
        return JobResult.OK; // Return based on actual implementation logic.
    }
}

 

Avatar

Community Advisor

Hi @Art_Bird 

  I just tested below mentioned code snippet with 6.4 and it is working fine. Can you compare you class with this?

package ********;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.event.jobs.JobManager;

@Component(immediate = true, label = "TEST")
@Service(value = TestEmailService.class)
public class TestEmailServiceImpl implements TestEmailService {


    
    @Reference
    private JobManager jobManager;
    
  
	@Override
    public List<String> sendEmail(Map<String, String> emailParams, String[] recipients) {
    	final List<String> failureList = new ArrayList<String>();
    	Map<String,Object> emailParamsObj = new HashMap<String,Object>();
    	emailParamsObj.putAll(emailParams);
    	emailParamsObj.put("recipients", String.join(",", recipients));
    	jobManager.addJob("test/email/job", emailParamsObj);
        return failureList;
    }
    
  
}

 

Avatar

Level 1

Hello,

 

I had the same problem. The reason for jobManager being null is wrong import:

 

this is bad:

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

 

this is good:

import org.osgi.service.component.annotations.Reference;

 

Avatar

Community Advisor

Please refer to https://techrevel.blog/2023/11/06/enhancing-efficiency-and-reliability-by-sling-jobs/#scheduled-jobs

 

It has the relevant code snippet.

We need to use org.apache.sling.event.jobs.JobManager instead of org.apache.sling.jobs.JobManager. Rest all stays the same


Aanchal Sikka