blob: 1981e770d999bb5facbcbf1cee43c12a375b4335 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.sling.event.impl.jobs;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.apache.sling.api.resource.Resource;
import org.slf4j.Logger;
/**
* The job topic traverser is an utility class to traverse all jobs
* of a specific topic in order of creation.
*
* The traverser can be used with two different callbacks,
* the resource callback is called with a resource object,
* the job callback with a job object created from the
* resource.
*/
public class JobTopicTraverser {
/**
* Callback called for each found job.
*/
public interface JobCallback {
/**
* Callback handle for a job.
* If the callback signals to stop traversing, the current minute is still
* processed completely (to ensure correct ordering of jobs).
* @param job The job to handle
* @return <code>true</code> If processing should continue, <code>false</code> otherwise.
*/
boolean handle(final JobImpl job);
}
/**
* Callback called for each found resource.
*/
public interface ResourceCallback {
/**
* Callback handle for a resource.
* The callback is called in sorted order on a minute base, all resources within a minute
* are not necessarily called in correct time order!
* If the callback signals to stop traversing, the traversal is stopped
* immediately.
* @param rsrc The resource to handle
* @return <code>true</code> If processing should continue, <code>false</code> otherwise.
*/
boolean handle(final Resource rsrc);
}
/**
* Traverse the topic and call the callback for each found job.
*
* Once the callback notifies to stop traversing by returning false, the current minute
* will be processed completely (to ensure correct ordering of jobs) and then the
* traversal stops.
*
* @param logger The logger to use for debug logging
* @param topicResource The topic resource
* @param handler The callback
*/
public static void traverse(final Logger logger,
final Resource topicResource,
final JobCallback handler) {
traverse(logger, topicResource, handler, null);
}
/**
* Traverse the topic and call the callback for each found resource.
*
* Once the callback notifies to stop traversing by returning false, the
* traversal stops.
*
* @param logger The logger to use for debug logging
* @param topicResource The topic resource
* @param handler The callback
*/
public static void traverse(final Logger logger,
final Resource topicResource,
final ResourceCallback handler) {
traverse(logger, topicResource, null, handler);
}
/**
* Internal method for traversal
* @param logger The logger to use for debug logging
* @param topicResource The topic resource
* @param jobHandler The job callback
* @param resourceHandler The resource callback
*/
private static void traverse(final Logger logger,
final Resource topicResource,
final JobCallback jobHandler,
final ResourceCallback resourceHandler) {
logger.debug("Processing topic {}", topicResource.getName().replace('.', '/'));
// now years
for(final Resource yearResource: Utility.getSortedChildren(logger, "year", topicResource)) {
logger.debug("Processing year {}", yearResource.getName());
// now months
for(final Resource monthResource: Utility.getSortedChildren(logger, "month", yearResource)) {
logger.debug("Processing month {}", monthResource.getName());
// now days
for(final Resource dayResource: Utility.getSortedChildren(logger, "day", monthResource)) {
logger.debug("Processing day {}", dayResource.getName());
// now hours
for(final Resource hourResource: Utility.getSortedChildren(logger, "hour", dayResource)) {
logger.debug("Processing hour {}", hourResource.getName());
// now minutes
for(final Resource minuteResource: Utility.getSortedChildren(logger, "minute", hourResource)) {
logger.debug("Processing minute {}", minuteResource.getName());
// now jobs
final List<JobImpl> jobs = new ArrayList<JobImpl>();
// we use an iterator to skip removed entries
// see SLING-4073
final Iterator<Resource> jobIter = minuteResource.listChildren();
while ( jobIter.hasNext() ) {
final Resource jobResource = jobIter.next();
if ( resourceHandler != null ) {
if ( !resourceHandler.handle(jobResource) ) {
return;
}
} else {
final JobImpl job = Utility.readJob(logger, jobResource);
if ( job != null ) {
logger.debug("Found job {}", jobResource.getName());
jobs.add(job);
}
}
}
if ( jobHandler != null ) {
Collections.sort(jobs);
boolean stop = false;
for(final JobImpl job : jobs) {
if ( !jobHandler.handle(job) ) {
stop = true;
}
}
if ( stop ) {
return;
}
}
}
}
}
}
}
}
}