This is an automated email from the ASF dual-hosted git repository.

martin_s pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/archiva-components.git


The following commit(s) were added to refs/heads/master by this push:
     new 9be22b9  Adding spring-quartz module
9be22b9 is described below

commit 9be22b999d56a4de6661102cb01ec4b25418ad40
Author: Martin Stockhammer <[email protected]>
AuthorDate: Sat Nov 23 21:53:22 2019 +0100

    Adding spring-quartz module
---
 pom.xml                                            |   1 +
 spring-quartz/pom.xml                              | 122 ++++++
 .../archiva/components/scheduler/AbstractJob.java  | 112 ++++++
 .../scheduler/CronExpressionValidator.java         | 446 +++++++++++++++++++++
 .../components/scheduler/DefaultJobListener.java   |  87 ++++
 .../components/scheduler/DefaultScheduler.java     | 187 +++++++++
 .../archiva/components/scheduler/Scheduler.java    |  60 +++
 .../configuration/SchedulerConfiguration.java      | 129 ++++++
 .../src/main/resources/META-INF/spring-context.xml |  34 ++
 .../src/site/asciidoc/index.adoc.vm                |  14 +-
 spring-quartz/src/site/site.xml                    |  34 ++
 .../scheduler/CronExpressionValidatorTest.java     | 131 ++++++
 .../archiva/components/scheduler/JobOne.java       |  48 +++
 .../components/scheduler/SchedulerTest.java        | 124 ++++++
 spring-quartz/src/test/resources/log4j2-test.xml   |  36 ++
 .../src/test/resources/spring-context.xml          |  38 ++
 src/site/asciidoc/index.adoc                       |   1 +
 17 files changed, 1593 insertions(+), 11 deletions(-)

diff --git a/pom.xml b/pom.xml
index 0f01f99..cfed84f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -70,6 +70,7 @@
     <module>spring-registry</module>
     <module>spring-cache</module>
     <module>spring-taskqueue</module>
+    <module>spring-quartz</module>
   </modules>
 
   <repositories>
diff --git a/spring-quartz/pom.xml b/spring-quartz/pom.xml
new file mode 100644
index 0000000..0f1f9b9
--- /dev/null
+++ b/spring-quartz/pom.xml
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.archiva.components</groupId>
+    <artifactId>archiva-components</artifactId>
+    <version>3.0-SNAPSHOT</version>
+  </parent>
+
+  <version>3.0-SNAPSHOT</version>
+  <artifactId>archiva-components-spring-quartz</artifactId>
+
+  <name>Archiva Components :: Spring Quartz</name>
+
+  <properties>
+    <site.staging.base>${project.parent.basedir}</site.staging.base>
+  </properties>
+
+
+  <url>${webUrl}/${project.artifactId}</url>
+
+  <dependencies>
+    <dependency>
+      <groupId>javax.inject</groupId>
+      <artifactId>javax.inject</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>javax.annotation</groupId>
+      <artifactId>javax.annotation-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-context</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.quartz-scheduler</groupId>
+      <artifactId>quartz</artifactId>
+      <version>2.2.1</version>
+      <exclusions>
+        <exclusion>
+          <groupId>c3p0</groupId>
+          <artifactId>c3p0</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-lang3</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-slf4j-impl</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-jcl</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <forkedProcessTimeoutInSeconds>60</forkedProcessTimeoutInSeconds>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+  <reporting>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+        <inherited>false</inherited>
+        <reportSets>
+          <reportSet>
+            <id>non-aggregate</id>
+            <reports>
+              <report>javadoc</report>
+            </reports>
+          </reportSet>
+        </reportSets>
+      </plugin>
+    </plugins>
+  </reporting>
+
+</project>
diff --git 
a/spring-quartz/src/main/java/org/apache/archiva/components/scheduler/AbstractJob.java
 
b/spring-quartz/src/main/java/org/apache/archiva/components/scheduler/AbstractJob.java
new file mode 100644
index 0000000..11d153c
--- /dev/null
+++ 
b/spring-quartz/src/main/java/org/apache/archiva/components/scheduler/AbstractJob.java
@@ -0,0 +1,112 @@
+package org.apache.archiva.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.quartz.JobDataMap;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.quartz.InterruptableJob;
+import org.quartz.UnableToInterruptJobException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Base class from which all <code>Job</code>s running in the
+ * scheduler should be derived from if they want access to the
+ * ServiceBroker.
+ *
+ * @author <a href="mailto:[email protected]";>Jason van Zyl</a>
+ */
+public abstract class AbstractJob
+    implements InterruptableJob
+{
+
+    private Logger log = LoggerFactory.getLogger( getClass() );
+
+    /**
+     * JobDataMap tag for the job's logger.
+     */
+    public static final String LOGGER = "JOB_LOGGER";
+
+    /**
+     * JobDataMap tag for the job's context.
+     */
+    public static final String CONTEXT = "JOB_CONTEXT";
+
+    /**
+     * JobDataMap tag for the job's service broker.
+     */
+    public static final String SERVICE_MANAGER = "JOB_SERVICE_MANAGER";
+
+    /**
+     * JobDataMap tag for the job's configuration.
+     */
+    public static final String EXECUTION_CONFIGURATION = 
"JOB_EXECUTION_CONFIGURATION";
+
+    /**
+     * Job Data Map
+     */
+    private JobDataMap jobDataMap;
+
+    private boolean interrupted;
+
+    /**
+     * Set Job Data Map
+     */
+    public void setJobDataMap( JobDataMap jobDataMap )
+    {
+        this.jobDataMap = jobDataMap;
+    }
+
+    /**
+     * Get Job Data Map
+     */
+    public JobDataMap getJobDataMap()
+    {
+        return jobDataMap;
+    }
+
+    /**
+     * Get the Logger.
+     */
+    public Logger getLogger()
+    {
+        return (Logger) getJobDataMap().get( LOGGER );
+    }
+
+
+    /**
+     * Execute the Job.
+     */
+    public abstract void execute( JobExecutionContext context )
+        throws JobExecutionException;
+
+    public boolean isInterrupted()
+    {
+        return interrupted;
+    }
+
+    public void interrupt()
+        throws UnableToInterruptJobException
+    {
+        interrupted = true;
+    }
+}
diff --git 
a/spring-quartz/src/main/java/org/apache/archiva/components/scheduler/CronExpressionValidator.java
 
b/spring-quartz/src/main/java/org/apache/archiva/components/scheduler/CronExpressionValidator.java
new file mode 100644
index 0000000..c00a9c0
--- /dev/null
+++ 
b/spring-quartz/src/main/java/org/apache/archiva/components/scheduler/CronExpressionValidator.java
@@ -0,0 +1,446 @@
+package org.apache.archiva.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.lang3.StringUtils;
+import org.quartz.CronScheduleBuilder;
+import org.quartz.CronTrigger;
+import org.quartz.TriggerBuilder;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.StringTokenizer;
+
+/**
+ * @author <a href="mailto:[email protected]";>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;
+        }
+    }
+}
diff --git 
a/spring-quartz/src/main/java/org/apache/archiva/components/scheduler/DefaultJobListener.java
 
b/spring-quartz/src/main/java/org/apache/archiva/components/scheduler/DefaultJobListener.java
new file mode 100644
index 0000000..74cb95f
--- /dev/null
+++ 
b/spring-quartz/src/main/java/org/apache/archiva/components/scheduler/DefaultJobListener.java
@@ -0,0 +1,87 @@
+package org.apache.archiva.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.quartz.Job;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.quartz.JobListener;
+
+/**
+ * Currently the role this class plays is set the value of the 
<code>JobDataMap</code>
+ * in the job so that the convenience methods for accessing the logger, 
context,
+ * service broker and configuration will work as expected.
+ *
+ * @author <a href="mailto:[email protected]";>Jason van Zyl</a>
+ *
+ */
+public class DefaultJobListener
+    implements JobListener
+{
+    /**
+     * <p>
+     * <p/>
+     * Get the name of the <code>JobListener</code>.</p>
+     */
+    public String getName()
+    {
+        return "DefaultJobLister";
+    }
+
+    /**
+     * <p>
+     * <p/>
+     * Called by the <code>{@link Scheduler}</code> when a <code>{@link 
Job}</code>
+     * is about to be executed (an associated <code>{@link 
org.quartz.Trigger}</code> has
+     * occured).</p>
+     */
+    public void jobToBeExecuted( JobExecutionContext context )
+    {
+        Job job = context.getJobInstance();
+
+        // Only attempt to set the ServiceBroker when we are dealing
+        // with subclasses AbstractJob.
+        if ( job instanceof AbstractJob )
+        {
+            ( (AbstractJob) job ).setJobDataMap( 
context.getJobDetail().getJobDataMap() );
+        }
+    }
+
+    public void jobExecutionVetoed( JobExecutionContext jobExecutionContext )
+    {
+    }
+
+    /**
+     * <p>
+     * <p/>
+     * Called by the <code>{@link Scheduler}</code> after a <code>{@link 
Job}</code>
+     * has been executed, and be for the associated <code>Trigger</code>'s
+     * <code>triggered(xx)</code> method has been called.</p>
+     */
+    public void jobWasExecuted( JobExecutionContext context, 
JobExecutionException jobException )
+    {
+        Job job = context.getJobInstance();
+
+        // Only attempt to null the ServiceBroker when we are dealing
+        // with subclasses AbstractJob.
+        if ( job instanceof AbstractJob )
+        {
+        }
+    }
+}
diff --git 
a/spring-quartz/src/main/java/org/apache/archiva/components/scheduler/DefaultScheduler.java
 
b/spring-quartz/src/main/java/org/apache/archiva/components/scheduler/DefaultScheduler.java
new file mode 100644
index 0000000..650c8a1
--- /dev/null
+++ 
b/spring-quartz/src/main/java/org/apache/archiva/components/scheduler/DefaultScheduler.java
@@ -0,0 +1,187 @@
+package org.apache.archiva.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.quartz.JobDetail;
+import org.quartz.JobKey;
+import org.quartz.JobListener;
+import org.quartz.Matcher;
+import org.quartz.SchedulerException;
+import org.quartz.SchedulerFactory;
+import org.quartz.Trigger;
+import org.quartz.TriggerListener;
+import org.quartz.impl.StdScheduler;
+import org.quartz.impl.StdSchedulerFactory;
+import org.quartz.utils.Key;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import java.util.Properties;
+
+public class DefaultScheduler
+    implements Scheduler
+{
+
+    private Logger log = LoggerFactory.getLogger( getClass() );
+
+    private Properties properties;
+
+    private StdScheduler scheduler;
+
+    public void scheduleJob( JobDetail jobDetail, Trigger trigger )
+        throws SchedulerException
+    {
+
+        if ( jobDetail == null || jobDetail.getKey() == null || 
jobDetail.getKey().getName() == null )
+        {
+            throw new SchedulerException( "No job or no job name - cannot 
schedule this job" );
+        }
+
+        if ( jobExists( jobDetail.getKey() ) )
+        {
+            log.warn( "Will not schedule this job as a job ({}:{}) already 
exists.",
+                      jobDetail.getKey().getName(), 
jobDetail.getKey().getGroup() );
+
+            return;
+        }
+
+        try
+        {
+            scheduler.scheduleJob( jobDetail, trigger );
+        }
+        catch ( SchedulerException e )
+        {
+            throw new SchedulerException( "Error scheduling job.", e );
+        }
+        catch ( Exception e )
+        {
+            throw new SchedulerException( "Error scheduling job (Verify your 
cron expression).", e );
+        }
+    }
+
+    public void addGlobalJobListener( JobListener listener )
+        throws SchedulerException
+    {
+        scheduler.getListenerManager().addJobListener( listener, new 
AllMatch() );
+    }
+
+    public void addGlobalTriggerListener( TriggerListener listener )
+        throws SchedulerException
+    {
+        scheduler.getListenerManager().addTriggerListener( listener, new 
AllMatch() );
+    }
+
+    private static class AllMatch<R extends Key<?>>
+        implements Matcher<R>
+    {
+        public boolean isMatch( R key )
+        {
+            return true;
+        }
+    }
+
+    @PostConstruct
+    public void initialize()
+        throws SchedulerException
+    {
+        SchedulerFactory factory = new StdSchedulerFactory( properties );
+
+        scheduler = (StdScheduler) factory.getScheduler();
+
+        scheduler.start();
+
+    }
+
+    @PreDestroy
+    public void stop()
+    {
+        scheduler.shutdown();
+    }
+
+    public void unscheduleJob( String jobName, String groupName )
+        throws SchedulerException
+    {
+        if ( jobName == null )
+        {
+            throw new SchedulerException( "Job name null - cannot unschedule 
job" );
+        }
+
+        try
+        {
+            if ( jobExists( jobName, groupName ) )
+            {
+                scheduler.deleteJob( new JobKey( jobName, groupName ) );
+            }
+        }
+        catch ( SchedulerException e )
+        {
+            throw new SchedulerException( "Error unscheduling job.", e );
+        }
+    }
+
+    public boolean interruptSchedule( String jobName, String groupName )
+        throws SchedulerException
+    {
+        try
+        {
+            return scheduler.interrupt( new JobKey( jobName, groupName ) );
+        }
+        catch ( Exception e )
+        {
+            throw new SchedulerException( "Can't interrup job \"" + jobName + 
"\".", e );
+        }
+    }
+
+    private boolean jobExists( String jobName, String jobGroup )
+        throws SchedulerException
+    {
+
+        return jobExists( new JobKey( jobName, jobGroup ) );
+    }
+
+    private boolean jobExists( JobKey jobKey )
+        throws SchedulerException
+    {
+        return scheduler.getJobDetail( jobKey ) != null;
+    }
+
+    public void shutdown( boolean waitForJobsToComplete )
+    {
+        log.info( "call shutdown waitForJobsToComplete : {}", 
waitForJobsToComplete );
+        scheduler.shutdown( waitForJobsToComplete );
+    }
+
+    public StdScheduler getScheduler()
+    {
+        return scheduler;
+    }
+
+    public void setProperties( Properties properties )
+    {
+        this.properties = properties;
+    }
+
+    public Properties getProperties()
+    {
+        return properties;
+    }
+}
diff --git 
a/spring-quartz/src/main/java/org/apache/archiva/components/scheduler/Scheduler.java
 
b/spring-quartz/src/main/java/org/apache/archiva/components/scheduler/Scheduler.java
new file mode 100644
index 0000000..49fd717
--- /dev/null
+++ 
b/spring-quartz/src/main/java/org/apache/archiva/components/scheduler/Scheduler.java
@@ -0,0 +1,60 @@
+package org.apache.archiva.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.quartz.JobDetail;
+import org.quartz.JobListener;
+import org.quartz.SchedulerException;
+import org.quartz.Trigger;
+import org.quartz.TriggerListener;
+
+import java.util.Properties;
+
+public interface Scheduler
+{
+
+    void scheduleJob( JobDetail jobDetail, Trigger trigger )
+        throws SchedulerException;
+
+    /**
+     * @param listener
+     */
+    void addGlobalJobListener( JobListener listener )
+        throws SchedulerException;
+
+    /**
+     * @param listener
+     */
+    void addGlobalTriggerListener( TriggerListener listener )
+        throws SchedulerException;
+
+    void unscheduleJob( String jobName, String groupName )
+        throws SchedulerException;
+
+    boolean interruptSchedule( String jobName, String groupName )
+        throws SchedulerException;
+
+    void setProperties( Properties properties );
+
+    Properties getProperties();
+
+    void shutdown( boolean waitForJobsToComplete );
+
+}
diff --git 
a/spring-quartz/src/main/java/org/apache/archiva/components/scheduler/configuration/SchedulerConfiguration.java
 
b/spring-quartz/src/main/java/org/apache/archiva/components/scheduler/configuration/SchedulerConfiguration.java
new file mode 100644
index 0000000..e9fcdec
--- /dev/null
+++ 
b/spring-quartz/src/main/java/org/apache/archiva/components/scheduler/configuration/SchedulerConfiguration.java
@@ -0,0 +1,129 @@
+package org.apache.archiva.components.scheduler.configuration;
+
+/*
+ * 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.archiva.components.scheduler.Scheduler;
+import org.quartz.impl.StdSchedulerFactory;
+import org.springframework.stereotype.Service;
+
+import javax.inject.Inject;
+
+/**
+ * Class to represent the configuration file for the proxy
+ *
+ * @author John Tolentino
+ */
+@Service
+public class SchedulerConfiguration
+{
+    @Inject
+    private Scheduler scheduler;
+
+    public String getInstanceName()
+    {
+        return scheduler.getProperties().getProperty( 
StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME );
+    }
+
+    public void setInstanceName( String instanceName )
+    {
+        scheduler.getProperties().setProperty( 
StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME, instanceName );
+    }
+
+    public String getInstanceId()
+    {
+        return scheduler.getProperties().getProperty( 
StdSchedulerFactory.PROP_SCHED_INSTANCE_ID );
+    }
+
+    public void setInstanceId( String InstanceId )
+    {
+        scheduler.getProperties().setProperty( 
StdSchedulerFactory.PROP_SCHED_INSTANCE_ID, InstanceId );
+    }
+
+    public String getThreadName()
+    {
+        return scheduler.getProperties().getProperty( 
StdSchedulerFactory.PROP_SCHED_THREAD_NAME );
+    }
+
+    public void setThreadName( String threadName )
+    {
+        scheduler.getProperties().setProperty( 
StdSchedulerFactory.PROP_SCHED_THREAD_NAME, threadName );
+    }
+
+    public String getIdleWaitTime()
+    {
+        return scheduler.getProperties().getProperty( 
StdSchedulerFactory.PROP_SCHED_IDLE_WAIT_TIME );
+    }
+
+    public void setIdleWaitTime( String idleWaitTime )
+    {
+        scheduler.getProperties().setProperty( 
StdSchedulerFactory.PROP_SCHED_IDLE_WAIT_TIME, idleWaitTime );
+    }
+
+    public String getDbFailureRetryInterval()
+    {
+        return scheduler.getProperties().getProperty( 
StdSchedulerFactory.PROP_SCHED_DB_FAILURE_RETRY_INTERVAL );
+    }
+
+    public void setDbFailureRetryInterval( String dbFailureRetryInterval )
+    {
+        scheduler.getProperties().setProperty( 
StdSchedulerFactory.PROP_SCHED_DB_FAILURE_RETRY_INTERVAL, 
dbFailureRetryInterval );
+    }
+
+    public String getClassLoadHelper()
+    {
+        return scheduler.getProperties().getProperty( 
StdSchedulerFactory.PROP_SCHED_CLASS_LOAD_HELPER_CLASS );
+    }
+
+    public void setClassLoadHelper( String classLoadHelper )
+    {
+        scheduler.getProperties().setProperty( 
StdSchedulerFactory.PROP_SCHED_CLASS_LOAD_HELPER_CLASS, classLoadHelper );
+    }
+
+    public String getContextKey()
+    {
+        return scheduler.getProperties().getProperty( 
StdSchedulerFactory.PROP_SCHED_CONTEXT_PREFIX );
+    }
+
+    public void setContextKey( String contextKey )
+    {
+        scheduler.getProperties().setProperty( 
StdSchedulerFactory.PROP_SCHED_CONTEXT_PREFIX, contextKey );
+    }
+
+    public String getUserTransactionURL()
+    {
+        return scheduler.getProperties().getProperty( 
StdSchedulerFactory.PROP_SCHED_USER_TX_URL );
+    }
+
+    public void setUserTransactionURL( String userTransactionURL )
+    {
+        scheduler.getProperties().setProperty( 
StdSchedulerFactory.PROP_SCHED_USER_TX_URL, userTransactionURL );
+    }
+
+    public String getWrapJobExecutionInUserTransaction()
+    {
+        return scheduler.getProperties().getProperty( 
StdSchedulerFactory.PROP_SCHED_WRAP_JOB_IN_USER_TX );
+    }
+
+    public void setWrapJobExecutionInUserTransaction( String 
wrapJobExecutionInUserTransaction )
+    {
+        scheduler.getProperties().setProperty( 
StdSchedulerFactory.PROP_SCHED_WRAP_JOB_IN_USER_TX, 
wrapJobExecutionInUserTransaction );
+    }
+
+}
diff --git a/spring-quartz/src/main/resources/META-INF/spring-context.xml 
b/spring-quartz/src/main/resources/META-INF/spring-context.xml
new file mode 100755
index 0000000..728c05c
--- /dev/null
+++ b/spring-quartz/src/main/resources/META-INF/spring-context.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+
+<!--
+  ~ 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.
+  -->
+<beans xmlns="http://www.springframework.org/schema/beans";
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xmlns:context="http://www.springframework.org/schema/context";
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+           http://www.springframework.org/schema/context 
+           
http://www.springframework.org/schema/context/spring-context-3.0.xsd";
+       default-lazy-init="true">
+
+  <context:annotation-config />
+  <context:component-scan 
+    base-package="org.apache.archiva.components.scheduler"/>
+ 
+</beans>
diff --git a/src/site/asciidoc/index.adoc 
b/spring-quartz/src/site/asciidoc/index.adoc.vm
similarity index 57%
copy from src/site/asciidoc/index.adoc
copy to spring-quartz/src/site/asciidoc/index.adoc.vm
index aab0a7e..bb78c13 100644
--- a/src/site/asciidoc/index.adoc
+++ b/spring-quartz/src/site/asciidoc/index.adoc.vm
@@ -19,16 +19,8 @@ NOTE: For help with the syntax of this file, see:
 http://maven.apache.org/guides/mini/guide-apt-format.html
 ////
 
-= Archiva Components
+= Spring Quartz
 
-Archiva Components bundles different independent component modules, that are 
used by Archiva and Redback, and are free
-to use by other projects.
+== Overview
 
-Currently the component project contains the following components:
-
-- 
link:archiva-components-expression-evaluator/index.html[expression-evaluatior]
-- link:archiva-components-graph/index.html[graph]
-- link:archiva-components-spring-apacheds/index.html[spring-apacheds]
-- link:archiva-components-spring-cache/index.html[spring-cache]
-- link:archiva-components-spring-registry/index.html[spring-registry]
-- link:archiva-components-spring-taskqueue/index.html[spring-taskqueue]
+This module provides a spring service for scheduling tasks with quartz.
diff --git a/spring-quartz/src/site/site.xml b/spring-quartz/src/site/site.xml
new file mode 100644
index 0000000..aee6237
--- /dev/null
+++ b/spring-quartz/src/site/site.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+  ~ 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.
+  -->
+
+<project name="Spring Quartz Component" >
+
+  <publishDate format="yyyy-MM-dd" position="none" />
+
+  <body>
+    <menu ref="modules" />
+    <menu ref="reports" />
+    <menu ref="ASF" />
+    <breadcrumbs>
+      <item name="Archiva Components" href="../index.html" />
+      <item name="Spring Quartz" href="index.html" />
+    </breadcrumbs>
+  </body>
+</project>
diff --git 
a/spring-quartz/src/test/java/org/apache/archiva/components/scheduler/CronExpressionValidatorTest.java
 
b/spring-quartz/src/test/java/org/apache/archiva/components/scheduler/CronExpressionValidatorTest.java
new file mode 100644
index 0000000..af07896
--- /dev/null
+++ 
b/spring-quartz/src/test/java/org/apache/archiva/components/scheduler/CronExpressionValidatorTest.java
@@ -0,0 +1,131 @@
+package org.apache.archiva.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 junit.framework.TestCase;
+import org.apache.archiva.components.scheduler.CronExpressionValidator;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import javax.inject.Inject;
+
+@RunWith( SpringJUnit4ClassRunner.class )
+@ContextConfiguration( locations = 
{"classpath*:/META-INF/spring-context.xml","classpath:/spring-context.xml"} )
+public class CronExpressionValidatorTest
+    extends TestCase
+{
+
+    @Inject
+    CronExpressionValidator validator;
+
+    @Test
+    public void testValidation()
+        throws Exception
+    {
+
+        assertTrue( validator.validate( "0 0 * * * ?" ) );
+
+        assertTrue( validator.validate( "0 0 * ? * *" ) );
+
+        assertFalse( validator.validate( "0 0 4-1 * * ?" ) );
+
+        assertTrue( validator.validate( "0 0 1-4 * * ?" ) );
+
+        assertTrue( validator.validate( "0 0,15,30,45 * * * ?" ) );
+
+        assertFalse( validator.validate( "0 0,45,15,30 * * * ?" ) );
+
+        assertTrue( validator.validate( "0 0 12 * * ?" ) );
+
+        assertTrue( validator.validate( "0 15 10 ? * *" ) );
+
+        assertTrue( validator.validate( "0 15 10 * * ?" ) );
+
+        assertTrue( validator.validate( "0 15 10 * * ? *" ) );
+
+        assertTrue( validator.validate( "0 15 10 * * ? 2005" ) );
+
+        assertFalse( validator.validate( "0 15 10 * * ? 2100" ) );
+
+        assertFalse( validator.validate( "0 15 10 * * ? 1969" ) );
+
+        assertTrue( validator.validate( "0 15 10 * * ? 2005-2007" ) );
+
+        assertFalse( validator.validate( "0 15 10 * * ? 2005-2100" ) );
+
+        assertFalse( validator.validate( "0 15 10 * * ? 1960-2010" ) );
+
+        assertTrue( validator.validate( "0 15 10 * * ? 2005/2" ) );
+
+        assertFalse( validator.validate( "0 15 10 * * ? 2100/3" ) );
+
+        assertFalse( validator.validate( "0 15 10 * * ? 1960/10" ) );
+
+        assertTrue( validator.validate( "0 * 14 * * ?" ) );
+
+        assertTrue( validator.validate( "0 0/5 14 * * ?" ) );
+
+        assertTrue( validator.validate( "0 0/5 14,18 * * ?" ) );
+
+        assertTrue( validator.validate( "0 0-5 14 * * ?" ) );
+
+        assertTrue( validator.validate( "0 10,44 14 ? 3 WED" ) );
+
+        assertTrue( validator.validate( "0 15 10 ? * MON-FRI" ) );
+
+        assertTrue( validator.validate( "0 15 10 15 * ?" ) );
+
+        assertTrue( validator.validate( "0 15 10 L * ?" ) );
+
+        assertFalse( validator.validate( "0 15 10 6#3 * ?" ) );
+
+        assertTrue( validator.validate( "0 15 10 15W * ?" ) );
+
+        assertFalse( validator.validate( "0 15 10 15W1 * ?" ) );
+
+        assertTrue( validator.validate( "0 15 10 ? * 6L" ) );
+
+        assertTrue( validator.validate( "0 15 10 ? * 6L" ) );
+
+        assertTrue( validator.validate( "0 15 10 ? * 6L 2002-2005" ) );
+
+        assertFalse( validator.validate( "0 15 10 ? * 6L3 2002-2005" ) );
+
+        assertTrue( validator.validate( "0 15 10 ? * 6#3" ) );
+
+        assertFalse( validator.validate( "0 15 10 ? * 6#" ) );
+
+        assertFalse( validator.validate( "0 15 10 ? * #3" ) );
+
+        assertFalse( validator.validate( "0 15 10 ? * 8#3" ) );
+
+        assertFalse( validator.validate( "0 15 10 ? * 6#6" ) );
+
+        assertFalse( validator.validate( "0 0" ) );
+
+        assertFalse( validator.validate( "0 0 * * * *" ) );
+
+        assertFalse( validator.validate( "a a a a a a" ) );
+
+        assertFalse( validator.validate( "0 0 0 ? 0 A" ) );
+    }
+}
diff --git 
a/spring-quartz/src/test/java/org/apache/archiva/components/scheduler/JobOne.java
 
b/spring-quartz/src/test/java/org/apache/archiva/components/scheduler/JobOne.java
new file mode 100644
index 0000000..61da6b0
--- /dev/null
+++ 
b/spring-quartz/src/test/java/org/apache/archiva/components/scheduler/JobOne.java
@@ -0,0 +1,48 @@
+package org.apache.archiva.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.quartz.Job;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Date;
+
+public class JobOne
+    implements Job
+{
+
+    private Logger logger = LoggerFactory.getLogger( getClass() );
+
+    public JobOne()
+    {
+    }
+
+    public void execute( JobExecutionContext context )
+        throws JobExecutionException
+    {
+        logger.info(
+            "    --- Testing Scheduler Component  --- {} executed.[{}]", 
context.getJobDetail().getDescription(), new Date() );
+
+    }
+
+}
diff --git 
a/spring-quartz/src/test/java/org/apache/archiva/components/scheduler/SchedulerTest.java
 
b/spring-quartz/src/test/java/org/apache/archiva/components/scheduler/SchedulerTest.java
new file mode 100644
index 0000000..1ea8ee2
--- /dev/null
+++ 
b/spring-quartz/src/test/java/org/apache/archiva/components/scheduler/SchedulerTest.java
@@ -0,0 +1,124 @@
+package org.apache.archiva.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 junit.framework.TestCase;
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.quartz.JobBuilder;
+import org.quartz.JobDataMap;
+import org.quartz.JobDetail;
+import org.quartz.JobExecutionContext;
+import org.quartz.Trigger;
+import org.quartz.TriggerBuilder;
+import org.quartz.TriggerListener;
+import org.quartz.impl.triggers.SimpleTriggerImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import javax.inject.Inject;
+
+@RunWith ( SpringJUnit4ClassRunner.class )
+@ContextConfiguration ( locations = { 
"classpath*:/META-INF/spring-context.xml", "classpath:/spring-context.xml" } )
+public class SchedulerTest
+    extends TestCase
+    implements TriggerListener
+{
+    private boolean triggerFired;
+
+    private Logger logger = LoggerFactory.getLogger( getClass() );
+
+    @Inject
+    private Scheduler scheduler;
+
+    @After
+    public void stop()
+    {
+        scheduler.shutdown( true );
+    }
+
+    @Test
+    public void testCreation()
+        throws Exception
+    {
+
+        assertNotNull( scheduler );
+
+        JobDataMap dataMap = new JobDataMap();
+
+        dataMap.put( "project", "continuum" );
+
+        JobDetail jobDetail = JobBuilder.newJob( JobOne.class )
+            .withIdentity( "job", "group" )
+            .setJobData( dataMap )
+            .build();
+
+        TriggerBuilder.newTrigger();
+
+        Trigger trigger = new SimpleTriggerImpl( "trigger", "group" );
+
+        scheduler.addGlobalTriggerListener( this );
+
+        scheduler.scheduleJob( jobDetail, trigger );
+
+        while ( !triggerFired )
+        {
+            Thread.sleep( 10 );
+        }
+        logger.info( "ok triggerFired" );
+    }
+
+    public void triggerComplete( Trigger trigger, JobExecutionContext context, 
int triggerInstructionCode )
+    {
+    }
+
+    public boolean vetoJobExecution( Trigger trigger, JobExecutionContext 
context )
+    {
+        return false;
+    }
+
+    public void triggerFired( Trigger trigger, JobExecutionContext context )
+    {
+        logger.info( "Trigger fired!" );
+
+        triggerFired = true;
+    }
+
+    public void triggerMisfired( Trigger trigger )
+    {
+    }
+
+    public void triggerComplete( Trigger trigger, JobExecutionContext context,
+                                 Trigger.CompletedExecutionInstruction 
triggerInstructionCode )
+    {
+        //To change body of implemented methods use File | Settings | File 
Templates.
+    }
+
+    public String getName()
+    {
+        return "foo";
+    }
+
+
+}
+
diff --git a/spring-quartz/src/test/resources/log4j2-test.xml 
b/spring-quartz/src/test/resources/log4j2-test.xml
new file mode 100644
index 0000000..7d42f70
--- /dev/null
+++ b/spring-quartz/src/test/resources/log4j2-test.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+  ~ 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.
+  -->
+<configuration>
+    <appenders>
+        <Console name="console" target="SYSTEM_OUT">
+            <PatternLayout pattern="[%t] %-5p %c %x - %m%n"/>
+        </Console>
+    </appenders>
+    <loggers>
+        <logger name="org.apache.archiva" level="info"/>
+        <logger name="org.apache.archiva.components.scheduler" level="info"/>
+
+        <root level="error" includeLocation="true">
+            <appender-ref ref="console"/>
+        </root>
+    </loggers>
+</configuration>
+
+
diff --git a/spring-quartz/src/test/resources/spring-context.xml 
b/spring-quartz/src/test/resources/spring-context.xml
new file mode 100755
index 0000000..ea64709
--- /dev/null
+++ b/spring-quartz/src/test/resources/spring-context.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+
+<!--
+  ~ 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.
+  -->
+<beans xmlns="http://www.springframework.org/schema/beans";
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd";>
+
+  <bean name="scheduler" 
class="org.apache.archiva.components.scheduler.DefaultScheduler">
+    <property name="properties">
+      <props>
+        <prop key="org.quartz.scheduler.instanceName">scheduler1</prop>
+        <prop 
key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>
+        <prop key="org.quartz.threadPool.threadCount">1</prop>
+        <prop key="org.quartz.threadPool.threadPriority">4</prop>
+        <prop 
key="org.quartz.jobStore.class">org.quartz.simpl.RAMJobStore</prop>
+      </props>
+    </property>
+  </bean>
+
+</beans>
diff --git a/src/site/asciidoc/index.adoc b/src/site/asciidoc/index.adoc
index aab0a7e..41744b4 100644
--- a/src/site/asciidoc/index.adoc
+++ b/src/site/asciidoc/index.adoc
@@ -32,3 +32,4 @@ Currently the component project contains the following 
components:
 - link:archiva-components-spring-cache/index.html[spring-cache]
 - link:archiva-components-spring-registry/index.html[spring-registry]
 - link:archiva-components-spring-taskqueue/index.html[spring-taskqueue]
+- link:archiva-components-spring-quartz/index.html[spring-quartz]

Reply via email to