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.
Solved! Go to Solution.
Topics help categorize Community content and increase your ability to discover relevant content.
Views
Replies
Total Likes
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.
}
}
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.
Hi @Art_Bird,
Can you please try to add immediate=true with your components, as this will immediately activate your components.
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
@Art_Bird Please try using OSGi R6 annotations as mentioned here:
https://stackoverflow.com/questions/66556618/how-to-create-jobconsumer-in-sling
Sample Code: https://github.com/Adobe-Consulting-Services/acs-aem-samples/blob/master/core/src/main/java/com/adob...
In MyCompoent, @Reference would be replaced with @OSGiService
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;
}
}
Views
Replies
Total Likes
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.
}
}
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;
}
}
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;
Views
Replies
Total Likes
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
Views
Replies
Total Likes
Views
Likes
Replies