/* * 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; } } } } } } } } }