/*-- * #%L * Cognifide Actions * %% * Copyright (C) 2015 Cognifide * %% * Licensed 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. * #L% */ package com.cognifide.actions.msg.replication.cleaner; import java.util.Calendar; import java.util.Iterator; import java.util.Map; import javax.jcr.query.Query; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Properties; import org.apache.felix.scr.annotations.Property; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.Service; import org.apache.jackrabbit.util.ISO8601; import org.apache.sling.api.resource.LoginException; 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.ResourceResolverFactory; import org.apache.sling.commons.osgi.PropertiesUtil; import org.apache.sling.commons.scheduler.Scheduler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.cognifide.actions.msg.replication.Configuration; /** * Remove old and unnecessary action entries. * */ //@formatter:off @Component(immediate = true, metatype = true) @Service @Properties({ @Property(name = "scheduler.expression", value = "0 0 3 * * ?"), @Property(name = UserGeneratedContentCleaner.TTL_NAME, intValue = UserGeneratedContentCleaner.TTL_DEFAULT, description = "TTL in hours") }) //@formatter:on public class UserGeneratedContentCleaner implements Runnable { private static final Logger LOG = LoggerFactory.getLogger(UserGeneratedContentCleaner.class); private static final String OLD_ACTIONS_QUERY = "SELECT * FROM [cq:Page] AS s " + "WHERE ISDESCENDANTNODE('%s') AND s.[jcr:created] < CAST('%s' AS DATE)"; final static String TTL_NAME = "ttl"; final static int TTL_DEFAULT = 48; private int ttl; @Reference private Configuration config; @Reference private Scheduler scheduler; @Reference private ResourceResolverFactory resolverFactory; @Activate void activate(Map<String, Object> config) throws Exception { ttl = PropertiesUtil.toInteger(config.get(TTL_NAME), TTL_DEFAULT); } @Override public void run() { final Calendar until = Calendar.getInstance(); until.add(Calendar.HOUR, -ttl); ResourceResolver resolver = null; try { resolver = resolverFactory.getAdministrativeResourceResolver(null); final Resource actionRoot = resolver.getResource(config.getActionRoot()); if (actionRoot != null) { final Resource yearNode = deleteChildrenUntil(actionRoot, until.get(Calendar.YEAR)); if (yearNode == null) { return; } final Resource monthNode = deleteChildrenUntil(yearNode, until.get(Calendar.MONTH) + 1); if (monthNode == null) { return; } final Resource dayNode = deleteChildrenUntil(monthNode, until.get(Calendar.DAY_OF_MONTH)); if (dayNode == null && ttl < 24) { return; } removeNodesFromDayFolder(dayNode, until); } } catch (PersistenceException e) { LOG.error("Can't clean UGC", e); } catch (LoginException e) { LOG.error("Can't get resolver", e); } finally { if (resolver != null && resolver.isLive()) { resolver.close(); } } } private void removeNodesFromDayFolder(Resource dayResource, Calendar until) throws PersistenceException { final ResourceResolver resolver = dayResource.getResourceResolver(); String oldActionsQuery = String.format(OLD_ACTIONS_QUERY, dayResource.getPath(), ISO8601.format(until)); Iterator<Resource> oldActions = resolver.findResources(oldActionsQuery, Query.JCR_SQL2); while (oldActions.hasNext()) { resolver.delete(oldActions.next()); } resolver.commit(); } /** * Get all children of given folder, check if child name can be parsed as Integer and remove if it name < * until. * * @param resource Parent resource. * @param until Remove children < until. * @return Child node with name == until. * @throws PersistenceException */ private Resource deleteChildrenUntil(Resource resource, int until) throws PersistenceException { final ResourceResolver resolver = resource.getResourceResolver(); boolean modified = false; Resource untilResource = null; final Iterator<Resource> iterator = resource.listChildren(); while (iterator.hasNext()) { final Resource childResource = iterator.next(); Integer name = parseIntegerOrNull(childResource.getName()); if (name == null) { continue; } else if (name < until) { modified = true; resolver.delete(childResource); } else if (name == until) { untilResource = childResource; } } if (modified) { resolver.commit(); } return untilResource; } private static Integer parseIntegerOrNull(String integer) { try { return Integer.parseInt(integer); } catch (NumberFormatException e) { return null; } } }