Package org.apache.sling.event.impl.jobs.tasks

Source Code of org.apache.sling.event.impl.jobs.tasks.CleanUpTask

/*
* 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.tasks;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;

import org.apache.sling.api.resource.PersistenceException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceUtil;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.event.impl.jobs.Utility;
import org.apache.sling.event.impl.jobs.config.JobManagerConfiguration;
import org.apache.sling.event.impl.jobs.config.TopologyCapabilities;
import org.apache.sling.event.impl.support.BatchResourceRemover;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Maintenance task...
*
* In the default configuration, this task runs every minute
*/
public class CleanUpTask {

    /** Logger. */
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    /** Job manager configuration. */
    private final JobManagerConfiguration configuration;

    /** We count the scheduler runs. */
    private volatile long schedulerRuns;

    /**
     * Constructor
     */
    public CleanUpTask(final JobManagerConfiguration config) {
        this.configuration = config;
    }

    /**
     * One maintenance run
     */
    public void run() {
        this.schedulerRuns++;
        logger.debug("Job manager maintenance: Starting #{}", this.schedulerRuns);

        final TopologyCapabilities topologyCapabilities = configuration.getTopologyCapabilities();
        if ( topologyCapabilities != null ) {
            // Clean up
            final String cleanUpUnassignedPath;;
            if ( topologyCapabilities.isLeader() ) {
                cleanUpUnassignedPath = this.configuration.getUnassignedJobsPath();
            } else {
                cleanUpUnassignedPath = null;
            }

            if ( schedulerRuns % 60 == 0 ) { // full clean up is done every hour
                this.fullEmptyFolderCleanup(topologyCapabilities, this.configuration.getLocalJobsPath());
                if ( cleanUpUnassignedPath != null ) {
                    this.fullEmptyFolderCleanup(topologyCapabilities, cleanUpUnassignedPath);
                }
            } else if ( schedulerRuns % 5 == 0 ) { // simple clean up every 5 minutes
                this.simpleEmptyFolderCleanup(topologyCapabilities, this.configuration.getLocalJobsPath());
                if ( cleanUpUnassignedPath != null ) {
                    this.simpleEmptyFolderCleanup(topologyCapabilities, cleanUpUnassignedPath);
                }
            }
        }

        // lock cleanup is done every minute
        this.lockCleanup(topologyCapabilities);
        logger.debug("Job manager maintenance: Finished #{}", this.schedulerRuns);
    }

    /**
     * Clean up the locks
     * All locks older than two minutes are removed
     */
    private void lockCleanup(final TopologyCapabilities caps) {
        if ( caps != null && caps.isLeader() ) {
            this.logger.debug("Cleaning up job resource tree: removing obsolete locks");
            final List<Resource> candidates = new ArrayList<Resource>();
            final ResourceResolver resolver = this.configuration.createResourceResolver();
            try {
                final Resource parentResource = resolver.getResource(this.configuration.getLocksPath());
                final Calendar startDate = Calendar.getInstance();
                startDate.add(Calendar.MINUTE, -2);

                this.lockCleanup(caps, candidates, parentResource, startDate);
                final BatchResourceRemover remover = new BatchResourceRemover();
                boolean batchRemove = true;
                for(final Resource lockResource : candidates) {
                    if ( caps.isActive() ) {
                        try {
                            if ( batchRemove ) {
                                remover.delete(lockResource);
                            } else {
                                resolver.delete(lockResource);
                                resolver.commit();
                            }
                        } catch ( final PersistenceException pe) {
                            batchRemove = false;
                            this.ignoreException(pe);
                            resolver.refresh();
                        }
                    } else {
                        break;
                    }
                }
                try {
                    resolver.commit();
                } catch ( final PersistenceException pe) {
                    this.ignoreException(pe);
                    resolver.refresh();
                }
            } finally {
                resolver.close();
            }
        }
    }

    /**
     * Recursive lock cleanup
     */
    private void lockCleanup(final TopologyCapabilities caps,
            final List<Resource> candidates,
            final Resource parentResource,
            final Calendar startDate) {
        for(final Resource childResource : parentResource.getChildren()) {
            if ( caps.isActive() ) {
                final ValueMap vm = ResourceUtil.getValueMap(childResource);
                final Calendar created = vm.get(Utility.PROPERTY_LOCK_CREATED, Calendar.class);
                if ( created != null ) {
                    // lock resource
                    if ( created.before(startDate) ) {
                        candidates.add(childResource);
                    }
                } else {
                    lockCleanup(caps, candidates, childResource, startDate);
                }
            } else {
                break;
            }
        }
    }

    /**
     * Simple empty folder removes empty folders for the last ten minutes
     * starting five minutes ago.
     * If folder for minute 59 is removed, we check the hour folder as well.
     */
    private void simpleEmptyFolderCleanup(final TopologyCapabilities caps, final String basePath) {
        this.logger.debug("Cleaning up job resource tree: looking for empty folders");
        final ResourceResolver resolver = this.configuration.createResourceResolver();
        try {
            final Calendar cleanUpDate = Calendar.getInstance();
            // go back five minutes
            cleanUpDate.add(Calendar.MINUTE, -5);

            final Resource baseResource = resolver.getResource(basePath);
            // sanity check - should never be null
            if ( baseResource != null ) {
                final Iterator<Resource> topicIter = baseResource.listChildren();
                while ( caps.isActive() && topicIter.hasNext() ) {
                    final Resource topicResource = topicIter.next();

                    for(int i = 0; i < 10; i++) {
                        if ( caps.isActive() ) {
                            final StringBuilder sb = new StringBuilder(topicResource.getPath());
                            sb.append('/');
                            sb.append(cleanUpDate.get(Calendar.YEAR));
                            sb.append('/');
                            sb.append(cleanUpDate.get(Calendar.MONTH) + 1);
                            sb.append('/');
                            sb.append(cleanUpDate.get(Calendar.DAY_OF_MONTH));
                            sb.append('/');
                            sb.append(cleanUpDate.get(Calendar.HOUR_OF_DAY));
                            sb.append('/');
                            sb.append(cleanUpDate.get(Calendar.MINUTE));
                            final String path = sb.toString();

                            final Resource dateResource = resolver.getResource(path);
                            if ( dateResource != null && !dateResource.listChildren().hasNext() ) {
                                resolver.delete(dateResource);
                                resolver.commit();
                            }
                            // check hour folder
                            if ( path.endsWith("59") ) {
                                final String hourPath = path.substring(0, path.length() - 3);
                                final Resource hourResource = resolver.getResource(hourPath);
                                if ( hourResource != null && !hourResource.listChildren().hasNext() ) {
                                    resolver.delete(hourResource);
                                    resolver.commit();
                                }
                            }
                            // go back another minute in time
                            cleanUpDate.add(Calendar.MINUTE, -1);
                        }
                    }
                }
            }

        } catch (final PersistenceException pe) {
            // in the case of an error, we just log this as a warning
            this.logger.warn("Exception during job resource tree cleanup.", pe);
        } finally {
            resolver.close();
        }
    }

    /**
     * Full cleanup - this scans all directories!
     */
    private void fullEmptyFolderCleanup(final TopologyCapabilities caps, final String basePath) {
        this.logger.debug("Cleaning up job resource tree: removing ALL empty folders");
        final ResourceResolver resolver = this.configuration.createResourceResolver();
        try {
            final Resource baseResource = resolver.getResource(basePath);
            // sanity check - should never be null
            if ( baseResource != null ) {
                final Calendar now = Calendar.getInstance();

                final Iterator<Resource> topicIter = baseResource.listChildren();
                while ( caps.isActive() && topicIter.hasNext() ) {
                    final Resource topicResource = topicIter.next();

                    // now years
                    final Iterator<Resource> yearIter = topicResource.listChildren();
                    while ( caps.isActive() && yearIter.hasNext() ) {
                        final Resource yearResource = yearIter.next();
                        final int year = Integer.valueOf(yearResource.getName());
                        final boolean oldYear = year < now.get(Calendar.YEAR);

                        // months
                        final Iterator<Resource> monthIter = yearResource.listChildren();
                        while ( caps.isActive() && monthIter.hasNext() ) {
                            final Resource monthResource = monthIter.next();
                            final int month = Integer.valueOf(monthResource.getName());
                            final boolean oldMonth = oldYear || month < (now.get(Calendar.MONTH) + 1);

                            // days
                            final Iterator<Resource> dayIter = monthResource.listChildren();
                            while ( caps.isActive() && dayIter.hasNext() ) {
                                final Resource dayResource = dayIter.next();
                                final int day = Integer.valueOf(dayResource.getName());
                                final boolean oldDay = oldMonth || day < now.get(Calendar.DAY_OF_MONTH);

                                // hours
                                final Iterator<Resource> hourIter = dayResource.listChildren();
                                while ( caps.isActive() && hourIter.hasNext() ) {
                                    final Resource hourResource = hourIter.next();
                                    final int hour = Integer.valueOf(hourResource.getName());
                                    final boolean oldHour = (oldDay && (oldMonth || now.get(Calendar.HOUR_OF_DAY) > 0)) || hour < (now.get(Calendar.HOUR_OF_DAY) -1);

                                    // we only remove minutes if the hour is old
                                    if ( oldHour ) {
                                        final Iterator<Resource> minuteIter = hourResource.listChildren();
                                        while ( caps.isActive() && minuteIter.hasNext() ) {
                                            final Resource minuteResource = minuteIter.next();

                                            // check if we can delete the minute
                                            if ( !minuteResource.listChildren().hasNext() ) {
                                                resolver.delete(minuteResource);
                                                resolver.commit();
                                            }
                                        }
                                    }

                                    // check if we can delete the hour
                                    if ( caps.isActive() && oldHour && !hourResource.listChildren().hasNext()) {
                                        resolver.delete(hourResource);
                                        resolver.commit();
                                    }
                                }
                                // check if we can delete the day
                                if ( caps.isActive() && oldDay && !dayResource.listChildren().hasNext()) {
                                    resolver.delete(dayResource);
                                    resolver.commit();
                                }
                            }

                            // check if we can delete the month
                            if ( caps.isActive() && oldMonth && !monthResource.listChildren().hasNext() ) {
                                resolver.delete(monthResource);
                                resolver.commit();
                            }
                        }

                        // check if we can delete the year
                        if ( caps.isActive() && oldYear && !yearResource.listChildren().hasNext() ) {
                            resolver.delete(yearResource);
                            resolver.commit();
                        }
                    }
                }
            }

        } catch (final PersistenceException pe) {
            // in the case of an error, we just log this as a warning
            this.logger.warn("Exception during job resource tree cleanup.", pe);
        } finally {
            resolver.close();
        }
    }

    /**
     * Helper method which just logs the exception in debug mode.
     * @param e
     */
    private void ignoreException(final Exception e) {
        if ( this.logger.isDebugEnabled() ) {
            this.logger.debug("Ignored exception " + e.getMessage(), e);
        }
    }
}
TOP

Related Classes of org.apache.sling.event.impl.jobs.tasks.CleanUpTask

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.