/*! * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * Copyright (c) 2002-2015 Pentaho Corporation.. All rights reserved. */ package org.pentaho.platform.plugin.services.repository; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.pentaho.platform.api.engine.IPentahoSession; import org.pentaho.platform.api.engine.IPentahoSystemListener; import org.pentaho.platform.api.scheduler2.ComplexJobTrigger; import org.pentaho.platform.api.scheduler2.IJobFilter; import org.pentaho.platform.api.scheduler2.IScheduler; import org.pentaho.platform.api.scheduler2.Job; import org.pentaho.platform.api.scheduler2.JobTrigger; import org.pentaho.platform.api.scheduler2.SchedulerException; import org.pentaho.platform.api.scheduler2.SimpleJobTrigger; import org.pentaho.platform.engine.core.system.PentahoSystem; import org.pentaho.platform.util.StringUtil; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * This is a 5.4-only class. To use it, update <tt>systemListeners.xml</tt> by adding the following section: * <pre> <bean id="repositoryCleanerSystemListener" class="org.pentaho.platform.plugin.services.repository.RepositoryCleanerSystemListener"> <property name="gcEnabled" value="true"/> <property name="execute" value="now"/> </bean> * </pre> * <tt>gcEnabled</tt> is a non-mandatory parameter, <tt>true</tt> by default. Use it to turn off the listener without * removing its description from the XML-file * <tt>execute</tt> is a parameter, that describes a time pattern of GC procedure. Supported values are: * <ul> * <li><tt>now</tt> - for one time execution</li> * <li><tt>weekly</tt> - for every Monday execution</li> * <li><tt>monthly</tt> - for every first day of month execution</li> * </ul> * Note, that periodic executions will be planned to start at 0:00. If an execution was not started at that time, * e.g. the server was shut down, then it will be started as soon as the scheduler is restored. * @author Andrey Khayrutdinov */ public class RepositoryCleanerSystemListener implements IPentahoSystemListener, IJobFilter { private final Log logger = LogFactory.getLog( RepositoryCleanerSystemListener.class ); enum Frequency { NOW( "now" ) { @Override public JobTrigger createTrigger() { return new SimpleJobTrigger( new Date(), new Date( Long.MAX_VALUE ), 0, 1 ); } }, WEEKLY( "weekly" ) { @Override public JobTrigger createTrigger() { // execute each first day of week at 0 hours return new ComplexJobTrigger( null, null, null, ComplexJobTrigger.SUNDAY, 0 ); } }, MONTHLY( "monthly" ) { @Override public JobTrigger createTrigger() { // execute each first day of month at 0 hours return new ComplexJobTrigger( null, null, 1, null, 0 ); } }; private final String value; Frequency( String value ) { this.value = value; } public abstract JobTrigger createTrigger(); public static Frequency fromString( String name ) { for ( Frequency frequency : values() ) { if ( frequency.value.equalsIgnoreCase( name ) ) { return frequency; } } return null; } String getValue() { return value; } } private boolean gcEnabled = true; private String execute; @Override public boolean startup( IPentahoSession session ) { IScheduler scheduler = PentahoSystem.get( IScheduler.class, "IScheduler2", session ); if ( scheduler == null ) { logger.error( "Cannot obtain an instance of IScheduler2" ); return false; } try { List<Job> jobs = scheduler.getJobs( this ); if ( gcEnabled ) { if ( jobs.isEmpty() ) { scheduleJob( scheduler ); } else { rescheduleIfNecessary( scheduler, jobs ); } } else { if ( !jobs.isEmpty() ) { unscheduleJob( scheduler, jobs ); } } } catch ( SchedulerException e ) { logger.error( "Scheduler error", e ); } return true; } private JobTrigger findJobTrigger() { if ( StringUtil.isEmpty( execute ) ) { logger.error( "\"execute\" property is not specified!" ); return null; } Frequency frequency = Frequency.fromString( execute ); if ( frequency == null ) { logger.error( "Unknown value for property \"execute\": " + execute ); return null; } return frequency.createTrigger(); } private void scheduleJob( IScheduler scheduler ) throws SchedulerException { JobTrigger trigger = findJobTrigger(); if ( trigger != null ) { logger.info( "Creating new job with trigger: " + trigger ); scheduler.createJob( RepositoryGcJob.JOB_NAME, RepositoryGcJob.class, null, trigger ); } } private void rescheduleIfNecessary( IScheduler scheduler, List<Job> jobs ) throws SchedulerException { JobTrigger trigger = findJobTrigger(); if ( trigger == null ) { return; } List<Job> matched = new ArrayList<Job>( jobs.size() ); for ( Job job : jobs ) { JobTrigger tr = job.getJobTrigger(); // unfortunately, JobTrigger does not override equals if ( trigger.getClass() != tr.getClass() ) { logger.info( "Removing job with id: " + job.getJobId() ); scheduler.removeJob( job.getJobId() ); } else { matched.add( job ); } } if ( matched.isEmpty() ) { logger.info( "Need to re-schedule job" ); scheduleJob( scheduler ); } } private void unscheduleJob( IScheduler scheduler, List<Job> jobs ) throws SchedulerException { for ( Job job : jobs ) { logger.info( "Removing job with id: " + job.getJobId() ); scheduler.removeJob( job.getJobId() ); } } @Override public void shutdown() { // nothing to do } @Override public boolean accept( Job job ) { return RepositoryGcJob.JOB_NAME.equals( job.getJobName() ); } public boolean isGcEnabled() { return gcEnabled; } public void setGcEnabled( boolean gcEnabled ) { this.gcEnabled = gcEnabled; } public String getExecute() { return execute; } public void setExecute( String execute ) { this.execute = execute; } }