blob: 54bbe41409a04dc13cd0dcde568ce7ee2875479a [file] [log] [blame]
package org.apache.archiva.redback.components.scheduler;
/*
* 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.
*/
import org.apache.commons.lang.StringUtils;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.TriggerBuilder;
import org.springframework.stereotype.Service;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
/**
* @author <a href="mailto:evenisse@codehaus.org">Emmanuel Venisse</a>
*
*/
@Service
public final class CronExpressionValidator
{
/**
* Validates a cron expression.
*
* @param cronExpression The expression to validate
* @return True is expression is valid
*/
public boolean validate( String cronExpression )
{
try
{
String[] cronParams = StringUtils.split( cronExpression );
if ( cronParams.length < 6 || cronParams.length > 7 )
{
return false;
}
CronTrigger cronTrigger = TriggerBuilder.newTrigger()
.withSchedule( CronScheduleBuilder.cronSchedule( cronExpression ) )
.build();
if ( cronParams[3].equals( "?" ) || cronParams[5].equals( "?" ) )
{
//Check seconds param
if ( !checkSecondsField( cronParams[0] ) )
{
return false;
}
//Check minutes param
if ( !checkMinutesField( cronParams[1] ) )
{
return false;
}
//Check hours param
if ( !checkHoursField( cronParams[2] ) )
{
return false;
}
//Check day-of-month param
if ( !checkDayOfMonthField( cronParams[3] ) )
{
return false;
}
//Check months param
if ( !checkMonthsField( cronParams[4] ) )
{
return false;
}
//Check day-of-week param
if ( !checkDayOfWeekField( cronParams[5] ) )
{
return false;
}
//Check year param
if ( cronParams.length == 7 )
{
if ( !checkYearField( cronParams[6] ) )
{
return false;
}
}
return true;
}
else
{
return false;
}
}
catch ( RuntimeException e )
{
return false;
}
}
private boolean checkSecondsField( String secondsField )
{
return checkField( secondsField, 0, 59 );
}
private boolean checkMinutesField( String minutesField )
{
return checkField( minutesField, 0, 59 );
}
private boolean checkHoursField( String hoursField )
{
return checkField( hoursField, 0, 23 );
}
private boolean checkDayOfMonthField( String dayOfMonthField )
{
if ( "?".equals( dayOfMonthField ) )
{
return true;
}
if ( dayOfMonthField.indexOf( 'L' ) >= 0 )
{
return checkFieldWithLetter( dayOfMonthField, "L", 1, 7, -1, -1 );
}
else if ( dayOfMonthField.indexOf( 'W' ) >= 0 )
{
return checkFieldWithLetter( dayOfMonthField, "W", 1, 31, -1, -1 );
}
else if ( dayOfMonthField.indexOf( 'C' ) >= 0 )
{
return checkFieldWithLetter( dayOfMonthField, "C", 1, 31, -1, -1 );
}
else
{
return checkField( dayOfMonthField, 1, 31 );
}
}
private boolean checkMonthsField( String monthsField )
{
monthsField = StringUtils.replace( monthsField, "JAN", "1" );
monthsField = StringUtils.replace( monthsField, "FEB", "2" );
monthsField = StringUtils.replace( monthsField, "MAR", "3" );
monthsField = StringUtils.replace( monthsField, "APR", "4" );
monthsField = StringUtils.replace( monthsField, "MAY", "5" );
monthsField = StringUtils.replace( monthsField, "JUN", "6" );
monthsField = StringUtils.replace( monthsField, "JUL", "7" );
monthsField = StringUtils.replace( monthsField, "AUG", "8" );
monthsField = StringUtils.replace( monthsField, "SEP", "9" );
monthsField = StringUtils.replace( monthsField, "OCT", "10" );
monthsField = StringUtils.replace( monthsField, "NOV", "11" );
monthsField = StringUtils.replace( monthsField, "DEC", "12" );
return checkField( monthsField, 1, 31 );
}
private boolean checkDayOfWeekField( String dayOfWeekField )
{
dayOfWeekField = StringUtils.replace( dayOfWeekField, "SUN", "1" );
dayOfWeekField = StringUtils.replace( dayOfWeekField, "MON", "2" );
dayOfWeekField = StringUtils.replace( dayOfWeekField, "TUE", "3" );
dayOfWeekField = StringUtils.replace( dayOfWeekField, "WED", "4" );
dayOfWeekField = StringUtils.replace( dayOfWeekField, "THU", "5" );
dayOfWeekField = StringUtils.replace( dayOfWeekField, "FRI", "6" );
dayOfWeekField = StringUtils.replace( dayOfWeekField, "SAT", "7" );
if ( "?".equals( dayOfWeekField ) )
{
return true;
}
if ( dayOfWeekField.indexOf( 'L' ) >= 0 )
{
return checkFieldWithLetter( dayOfWeekField, "L", 1, 7, -1, -1 );
}
else if ( dayOfWeekField.indexOf( 'C' ) >= 0 )
{
return checkFieldWithLetter( dayOfWeekField, "C", 1, 7, -1, -1 );
}
else if ( dayOfWeekField.indexOf( '#' ) >= 0 )
{
return checkFieldWithLetter( dayOfWeekField, "#", 1, 7, 1, 5 );
}
else
{
return checkField( dayOfWeekField, 1, 7 );
}
}
private boolean checkYearField( String yearField )
{
return checkField( yearField, 1970, 2099 );
}
private boolean checkField( String secondsField, int minimal, int maximal )
{
if ( secondsField.indexOf( '-' ) > -1 )
{
String startValue = secondsField.substring( 0, secondsField.indexOf( "-" ) );
String endValue = secondsField.substring( secondsField.indexOf( "-" ) + 1 );
if ( !( checkIntValue( startValue, minimal, maximal ) && checkIntValue( endValue, minimal, maximal ) ) )
{
return false;
}
try
{
int startVal = Integer.parseInt( startValue );
int endVal = Integer.parseInt( endValue );
return endVal > startVal;
}
catch ( NumberFormatException e )
{
return false;
}
}
else if ( secondsField.indexOf( ',' ) > -1 )
{
return checkListField( secondsField, minimal, maximal );
}
else if ( secondsField.indexOf( '/' ) > -1 )
{
return checkIncrementField( secondsField, minimal, maximal );
}
else if ( secondsField.indexOf( '*' ) != -1 )
{
return true;
}
else
{
return checkIntValue( secondsField, minimal, maximal );
}
}
private boolean checkFieldWithLetter( String value, String letter, int minimalBefore, int maximalBefore,
int minimalAfter, int maximalAfter )
{
boolean canBeAlone = false;
boolean canHaveIntBefore = false;
boolean canHaveIntAfter = false;
boolean mustHaveIntBefore = false;
boolean mustHaveIntAfter = false;
if ( "L".equals( letter ) )
{
canBeAlone = true;
canHaveIntBefore = true;
canHaveIntAfter = false;
mustHaveIntBefore = false;
mustHaveIntAfter = false;
}
if ( "W".equals( letter ) || "C".equals( letter ) )
{
canBeAlone = false;
canHaveIntBefore = true;
canHaveIntAfter = false;
mustHaveIntBefore = true;
mustHaveIntAfter = false;
}
if ( "#".equals( letter ) )
{
canBeAlone = false;
canHaveIntBefore = true;
canHaveIntAfter = true;
mustHaveIntBefore = true;
mustHaveIntAfter = true;
}
String beforeLetter = "";
String afterLetter = "";
if ( value.indexOf( letter ) >= 0 )
{
beforeLetter = value.substring( 0, value.indexOf( letter ) );
}
if ( !value.endsWith( letter ) )
{
afterLetter = value.substring( value.indexOf( letter ) + 1 );
}
if ( value.indexOf( letter ) >= 0 )
{
if ( letter.equals( value ) )
{
return canBeAlone;
}
if ( canHaveIntBefore )
{
if ( mustHaveIntBefore && beforeLetter.length() == 0 )
{
return false;
}
if ( !checkIntValue( beforeLetter, minimalBefore, maximalBefore, true ) )
{
return false;
}
}
else
{
if ( beforeLetter.length() > 0 )
{
return false;
}
}
if ( canHaveIntAfter )
{
if ( mustHaveIntAfter && afterLetter.length() == 0 )
{
return false;
}
if ( !checkIntValue( afterLetter, minimalAfter, maximalAfter, true ) )
{
return false;
}
}
else
{
if ( afterLetter.length() > 0 )
{
return false;
}
}
}
return true;
}
private boolean checkIncrementField( String value, int minimal, int maximal )
{
String start = value.substring( 0, value.indexOf( '/' ) );
String increment = value.substring( value.indexOf( '/' ) + 1 );
if ( !"*".equals( start ) )
{
return checkIntValue( start, minimal, maximal ) && checkIntValue( increment, minimal, maximal, false );
}
else
{
return checkIntValue( increment, minimal, maximal );
}
}
private boolean checkListField( String value, int minimal, int maximal )
{
StringTokenizer st = new StringTokenizer( value, "," );
List<String> values = new ArrayList<>();
while ( st.hasMoreTokens() )
{
values.add( st.nextToken() );
}
int previousValue = -1;
for ( Iterator<String> i = values.iterator(); i.hasNext(); )
{
String currentValue = i.next();
if ( !checkIntValue( currentValue, minimal, maximal ) )
{
return false;
}
try
{
int val = Integer.parseInt( currentValue );
if ( val <= previousValue )
{
return false;
}
else
{
previousValue = val;
}
}
catch ( NumberFormatException e )
{
// we have always an int
}
}
return true;
}
private boolean checkIntValue( String value, int minimal, int maximal )
{
return checkIntValue( value, minimal, maximal, true );
}
private static boolean checkIntValue( String value, int minimal, int maximal, boolean checkExtremity )
{
try
{
int val = Integer.parseInt( value );
if ( checkExtremity )
{
if ( val < minimal || val > maximal )
{
return false;
}
}
return true;
}
catch ( NumberFormatException e )
{
return false;
}
}
}