You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@click.apache.org by me...@apache.org on 2009/05/13 15:45:16 UTC

svn commit: r774364 - in /incubator/click/trunk/click: ./ documentation/docs/ examples/ examples/src/ examples/src/org/apache/click/examples/page/quartz/ examples/src/org/apache/click/examples/quartz/ examples/src/org/apache/click/examples/util/ exampl...

Author: medgar
Date: Wed May 13 13:45:15 2009
New Revision: 774364

URL: http://svn.apache.org/viewvc?rev=774364&view=rev
Log:
added Quartz scheduler integration example

Added:
    incubator/click/trunk/click/examples/src/org/apache/click/examples/page/quartz/
    incubator/click/trunk/click/examples/src/org/apache/click/examples/page/quartz/QuartzJobAndTriggerPage.java
    incubator/click/trunk/click/examples/src/org/apache/click/examples/page/quartz/QuartzJobSchedulerPage.java
    incubator/click/trunk/click/examples/src/org/apache/click/examples/page/quartz/package.html
    incubator/click/trunk/click/examples/src/org/apache/click/examples/quartz/
    incubator/click/trunk/click/examples/src/org/apache/click/examples/quartz/ExampleJob.java
    incubator/click/trunk/click/examples/src/org/apache/click/examples/quartz/JobAndSimpleTrigger.java
    incubator/click/trunk/click/examples/src/org/apache/click/examples/quartz/SchedulerService.java
    incubator/click/trunk/click/examples/src/org/apache/click/examples/quartz/package.html
    incubator/click/trunk/click/examples/src/org/apache/click/examples/util/DatabaseInitListener.java
      - copied, changed from r769101, incubator/click/trunk/click/examples/src/org/apache/click/examples/util/DatabaseInitFilter.java
    incubator/click/trunk/click/examples/src/quartz.properties
    incubator/click/trunk/click/examples/webapp/quartz/
    incubator/click/trunk/click/examples/webapp/quartz/quartz-job-and-trigger.htm
    incubator/click/trunk/click/examples/webapp/quartz/quartz-job-scheduler.htm
Removed:
    incubator/click/trunk/click/examples/src/org/apache/click/examples/util/DatabaseInitFilter.java
Modified:
    incubator/click/trunk/click/.classpath
    incubator/click/trunk/click/documentation/docs/roadmap-changes.html
    incubator/click/trunk/click/examples/build.xml
    incubator/click/trunk/click/examples/src/click-page.properties
    incubator/click/trunk/click/examples/webapp/WEB-INF/lib/   (props changed)
    incubator/click/trunk/click/examples/webapp/WEB-INF/menu.xml
    incubator/click/trunk/click/examples/webapp/WEB-INF/web.xml

Modified: incubator/click/trunk/click/.classpath
URL: http://svn.apache.org/viewvc/incubator/click/trunk/click/.classpath?rev=774364&r1=774363&r2=774364&view=diff
==============================================================================
--- incubator/click/trunk/click/.classpath (original)
+++ incubator/click/trunk/click/.classpath Wed May 13 13:45:15 2009
@@ -31,5 +31,6 @@
 	<classpathentry kind="lib" path="lib/velocity-1.6.2.jar"/>
 	<classpathentry kind="lib" path="examples/webapp/WEB-INF/lib/geronimo-annotation_1.0_spec-1.1.1.jar"/>
 	<classpathentry kind="lib" path="examples/webapp/WEB-INF/lib/acegi-security-1.0.7.jar"/>
+	<classpathentry kind="lib" path="examples/webapp/WEB-INF/lib/quartz-all-1.6.3.jar"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>

Modified: incubator/click/trunk/click/documentation/docs/roadmap-changes.html
URL: http://svn.apache.org/viewvc/incubator/click/trunk/click/documentation/docs/roadmap-changes.html?rev=774364&r1=774363&r2=774364&view=diff
==============================================================================
--- incubator/click/trunk/click/documentation/docs/roadmap-changes.html (original)
+++ incubator/click/trunk/click/documentation/docs/roadmap-changes.html Wed May 13 13:45:15 2009
@@ -117,6 +117,10 @@
           Reusable Panel</a> is an example of a reusable Panel
           which provides a Form for capturing Client details.
       </li>
+        <a target="_blank" class="external" href="http://www.avoka.com/click-examples/quartz/quartz-job-scheduler.htm">
+          Quartz Job Scheduler</a> is an example demonstrating how to integrate the Quartz Job Scheduling framework
+          into a Click application.
+      </li>
     </ul>
 
     <div style="margin-left: -2em; margin-top: 1.5em; margin-bottom: 1em;">

Modified: incubator/click/trunk/click/examples/build.xml
URL: http://svn.apache.org/viewvc/incubator/click/trunk/click/examples/build.xml?rev=774364&r1=774363&r2=774364&view=diff
==============================================================================
--- incubator/click/trunk/click/examples/build.xml (original)
+++ incubator/click/trunk/click/examples/build.xml Wed May 13 13:45:15 2009
@@ -37,6 +37,7 @@
    <property name="jar.log4j" value="log4j-1.2.14.jar"/>
    <property name="jar.logging" value="commons-logging-1.0.4.jar"/>
    <property name="jar.poi" value="poi-3.0-FINAL.jar"/>
+   <property name="jar.quartz" value="quartz-all-1.6.3.jar"/>
    <property name="jar.oro" value="oro-2.0.8.jar"/>
    <property name="jar.servlet" value="servlet-api-2.3.jar"/>
    <property name="jar.spring" value="spring-2.5.6.jar"/>
@@ -171,6 +172,10 @@
    	       dest="${dir.weblib}/${jar.annotation}"
    		   verbose="true"
    	       usetimestamp="true"/>
+   	  <get src="http://mirrors.ibiblio.org/pub/mirrors/maven2/opensymphony/quartz-all/1.6.3/${jar.quartz}"
+   	       dest="${dir.weblib}/${jar.quartz}"
+   		   verbose="true"
+   	       usetimestamp="true"/>
    </target>
    
 

Modified: incubator/click/trunk/click/examples/src/click-page.properties
URL: http://svn.apache.org/viewvc/incubator/click/trunk/click/examples/src/click-page.properties?rev=774364&r1=774363&r2=774364&view=diff
==============================================================================
--- incubator/click/trunk/click/examples/src/click-page.properties (original)
+++ incubator/click/trunk/click/examples/src/click-page.properties Wed May 13 13:45:15 2009
@@ -17,6 +17,10 @@
 
 version=2.1.0
 
+deleteConfirm=Are you sure you want to remove this {0}?
+
 invalid.form.submit=You have made an invalid form submission.
 
+jobSchedulerNotAvailableMsg=The job scheduler is not available
+
 usernameExistsError=Username already exists, please select another account username
\ No newline at end of file

Added: incubator/click/trunk/click/examples/src/org/apache/click/examples/page/quartz/QuartzJobAndTriggerPage.java
URL: http://svn.apache.org/viewvc/incubator/click/trunk/click/examples/src/org/apache/click/examples/page/quartz/QuartzJobAndTriggerPage.java?rev=774364&view=auto
==============================================================================
--- incubator/click/trunk/click/examples/src/org/apache/click/examples/page/quartz/QuartzJobAndTriggerPage.java (added)
+++ incubator/click/trunk/click/examples/src/org/apache/click/examples/page/quartz/QuartzJobAndTriggerPage.java Wed May 13 13:45:15 2009
@@ -0,0 +1,220 @@
+/*
+ * 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.click.examples.page.quartz;
+
+import javax.servlet.ServletContext;
+
+import org.apache.click.ActionListener;
+import org.apache.click.Control;
+import org.apache.click.control.FieldSet;
+import org.apache.click.control.Form;
+import org.apache.click.control.Option;
+import org.apache.click.control.Select;
+import org.apache.click.control.Submit;
+import org.apache.click.control.TextField;
+import org.apache.click.examples.page.BorderPage;
+import org.apache.click.examples.quartz.JobAndSimpleTrigger;
+import org.apache.click.examples.quartz.SchedulerService;
+import org.apache.click.extras.control.DateField;
+import org.quartz.JobDetail;
+import org.quartz.ee.servlet.QuartzInitializerListener;
+import org.quartz.impl.StdSchedulerFactory;
+
+/**
+ * Provides a Quartz Job and Simple Trigger edit page.
+ *
+ * @author Malcolm Edgar
+ */
+public class QuartzJobAndTriggerPage extends BorderPage {
+
+    private Form form = new Form("form");
+    private TextField nameField = new TextField("name", true);
+    private TextField classnameField = new TextField("classname", true);
+    private TextField descriptionField = new TextField("description");
+    private DateField startDateField = new DateField("startDate", true);
+    private DateField endDateField = new DateField("endDate");
+    private Select repeatCountField = new Select("repeatCount", "Repeat");
+    private Select repeatIntervalField = new Select("repeatInterval");
+
+    private SchedulerService schedulerService;
+
+    public QuartzJobAndTriggerPage() {
+
+        // Form
+        addControl(form);
+        form.setDefaultFieldSize(80);
+
+        FieldSet fieldSet = new FieldSet("Job and Trigger");
+        form.add(fieldSet);
+
+        nameField.setMaxLength(80);
+        fieldSet.add(nameField);
+
+        fieldSet.add(classnameField);
+
+        descriptionField.setMaxLength(120);
+        fieldSet.add(descriptionField);
+
+        fieldSet.add(startDateField);
+
+        fieldSet.add(endDateField);
+
+        repeatCountField.add(new Option("-1", "Run continuously"));
+        repeatCountField.add(new Option("0", "Run once"));
+        repeatCountField.add(new Option("1", "Repeat 1"));
+        repeatCountField.add(new Option("2", "Repeat 2"));
+        repeatCountField.add(new Option("3", "Repeat 3"));
+        repeatCountField.add(new Option("4", "Repeat 4"));
+        repeatCountField.add(new Option("5", "Repeat 5"));
+        repeatCountField.add(new Option("10", "Repeat 10"));
+        repeatCountField.add(new Option("20", "Repeat 20"));
+        repeatCountField.setValue("-1");
+        fieldSet.add(repeatCountField);
+
+        repeatIntervalField.add(new Option("60000", "1 minute"));
+        repeatIntervalField.add(new Option("120000", "2 minutes"));
+        repeatIntervalField.add(new Option("300000", "5 minutes"));
+        repeatIntervalField.add(new Option("600000", "10 minutes"));
+        repeatIntervalField.add(new Option("900000", "15 minutes"));
+        repeatIntervalField.add(new Option("1800000", "30 minutes"));
+        repeatIntervalField.add(new Option("3600000", "1 hour"));
+        repeatIntervalField.add(new Option("7200000", "2 hours"));
+        repeatIntervalField.add(new Option("10800000", "3 hours"));
+        repeatIntervalField.add(new Option("21600000", "6 hours"));
+        repeatIntervalField.add(new Option("43200000", "12 hours"));
+        repeatIntervalField.add(new Option("86400000", "24 hours"));
+        repeatIntervalField.setValue("3600000");
+        fieldSet.add(repeatIntervalField);
+
+        Submit saveSubmit = new Submit("Save");
+        saveSubmit.setActionListener(new ActionListener(){
+            public boolean onAction(Control source) {
+                return onSaveClick();
+            }
+        });
+        form.add(saveSubmit);
+
+        Submit cancelSubmit = new Submit("Cancel");
+        cancelSubmit.setActionListener(new ActionListener(){
+            public boolean onAction(Control source) {
+                setRedirect(QuartzJobSchedulerPage.class);
+                return false;
+            }
+        });
+        form.add(cancelSubmit);
+    }
+
+    // Event Handlers ---------------------------------------------------------
+
+    @Override
+    public void onInit() {
+        super.onInit();
+
+        if (getSchedulerService() != null) {
+            String name = getContext().getRequestParameter("job.name");
+            if (name != null) {
+                JobAndSimpleTrigger jat = getSchedulerService().getJobAndTrigger(name);
+
+                if (jat != null) {
+                    nameField.setReadonly(true);
+                    nameField.setAttribute("class", "readonly");
+                    form.copyFrom(jat.getJob());
+                    classnameField.setValue(jat.getJob().getJobClass().getName());
+                    startDateField.setDate(jat.getTrigger().getStartTime());
+                    endDateField.setDate(jat.getTrigger().getEndTime());
+                    repeatCountField.setValue("" + jat.getTrigger().getRepeatCount());
+                    repeatIntervalField.setValue("" + jat.getTrigger().getRepeatInterval());
+                }
+            }
+
+        } else {
+            setFlashMessage(getMessage("jobSchedulerNotAvailableMsg"));
+            form.setDisabled(true);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public boolean onSaveClick() {
+        if (form.isValid()) {
+            String name = nameField.getValue();
+
+            Class jobClass = null;
+            try {
+                jobClass = Class.forName(classnameField.getValue());
+            } catch (Exception e) {
+                classnameField.setError("Could not find class for classname");
+                return true;
+            }
+
+            if (getSchedulerService().getJobDetail(name) != null) {
+                getSchedulerService().deleteJob(name);
+            }
+
+            JobDetail jobDetail = new JobDetail();
+            form.copyTo(jobDetail);
+            jobDetail.setJobClass(jobClass);
+
+            long intervalMs = Long.parseLong(repeatIntervalField.getValue());
+
+            getSchedulerService().scheduleJob(jobDetail,
+                    startDateField.getDate(),
+                    endDateField.getDate(),
+                    Integer.parseInt(repeatCountField.getValue()),
+                    intervalMs);
+
+            setFlashMessage("Saved job '" + jobDetail.getName() + "'");
+
+            setRedirect(QuartzJobSchedulerPage.class);
+            return false;
+        }
+        return true;
+    }
+
+    // Protected Methods ------------------------------------------------------
+
+    /**
+     * Set a flash attribute message with the given message string.
+     *
+     * @param message the flash attribute message to display
+     */
+    protected void setFlashMessage(String message) {
+        getContext().setFlashAttribute("flash", message);
+    }
+
+    /**
+     * Return the scheduler service.
+     *
+     * @return the scheduler service
+     */
+    protected SchedulerService getSchedulerService() {
+        if (schedulerService == null) {
+            ServletContext servletContext = getContext().getServletContext();
+
+            StdSchedulerFactory schedulerFactory = (StdSchedulerFactory)
+                servletContext.getAttribute(QuartzInitializerListener.QUARTZ_FACTORY_KEY);
+
+            if (schedulerFactory != null) {
+                schedulerService = new SchedulerService(schedulerFactory);
+            }
+        }
+
+        return schedulerService;
+    }
+
+}

Added: incubator/click/trunk/click/examples/src/org/apache/click/examples/page/quartz/QuartzJobSchedulerPage.java
URL: http://svn.apache.org/viewvc/incubator/click/trunk/click/examples/src/org/apache/click/examples/page/quartz/QuartzJobSchedulerPage.java?rev=774364&view=auto
==============================================================================
--- incubator/click/trunk/click/examples/src/org/apache/click/examples/page/quartz/QuartzJobSchedulerPage.java (added)
+++ incubator/click/trunk/click/examples/src/org/apache/click/examples/page/quartz/QuartzJobSchedulerPage.java Wed May 13 13:45:15 2009
@@ -0,0 +1,310 @@
+/*
+ * 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.click.examples.page.quartz;
+
+import java.util.Collections;
+import java.util.List;
+
+import javax.servlet.ServletContext;
+
+import org.apache.cayenne.query.Ordering;
+import org.apache.click.ActionListener;
+import org.apache.click.Context;
+import org.apache.click.Control;
+import org.apache.click.control.ActionButton;
+import org.apache.click.control.ActionLink;
+import org.apache.click.control.Column;
+import org.apache.click.control.Decorator;
+import org.apache.click.control.PageLink;
+import org.apache.click.control.Table;
+import org.apache.click.examples.page.BorderPage;
+import org.apache.click.examples.quartz.JobAndSimpleTrigger;
+import org.apache.click.examples.quartz.SchedulerService;
+import org.apache.click.util.HtmlStringBuffer;
+import org.quartz.Trigger;
+import org.quartz.ee.servlet.QuartzInitializerListener;
+import org.quartz.impl.StdSchedulerFactory;
+
+/**
+ * Provides a Quartz Job Schedule search page.
+ *
+ * @author Malcolm Edgar
+ */
+public class QuartzJobSchedulerPage extends BorderPage {
+
+    private static final String DATE_FORMAT = "{0,date,hh:mm a d MMM yy }";
+
+    /** The auto bound refresh button. */
+    public ActionButton refresh = new ActionButton("refresh");
+
+    // Private Variables ------------------------------------------------------
+
+    private Table table = new Table("table");
+    private PageLink editLink = new PageLink("edit", QuartzJobAndTriggerPage.class);
+    private ActionLink pauseLink = new ActionLink("pause");
+    private ActionLink resumeLink = new ActionLink("resume");
+    private ActionLink triggerLink = new ActionLink("trigger");
+    private ActionLink interruptLink = new ActionLink("interrupt");
+    private ActionLink deleteLink = new ActionLink("delete");
+    private ActionButton newJob = new ActionButton("newJob");
+
+    private SchedulerService schedulerService;
+
+    // Constructor ------------------------------------------------------------
+
+    public QuartzJobSchedulerPage() {
+
+        // Table
+        addControl(table);
+        table.setAttribute("class", Table.CLASS_SIMPLE);
+        table.setSortable(true);
+        table.setStyle("margin-left", "0.25em;");
+
+        // Define columns
+
+        Column column = new Column("job.name", "Job Name");
+        column.setTitleProperty("job.description");
+        table.addColumn(column);
+
+        final Column statusColumn = new Column("triggerStateAsString", "Status");
+        statusColumn.setEscapeHtml(false);
+        statusColumn.setDecorator(new Decorator() {
+            public String render(Object row, Context context) {
+                JobAndSimpleTrigger jobAndTrigger = (JobAndSimpleTrigger) row;
+                switch(jobAndTrigger.getTriggerState()){
+                case Trigger.STATE_NONE:
+                    return "<span style='color:red'>None</span>";
+                case Trigger.STATE_NORMAL:
+                    return "<span style='color:blue'>Normal</span>";
+                case Trigger.STATE_PAUSED:
+                    return "<span style='color:red'>Paused</span>";
+                case Trigger.STATE_BLOCKED:
+                    return "<span style='color:green'>Running</span>";
+                case Trigger.STATE_COMPLETE:
+                    return "<span style='color:black'>Complete</span>";
+                case Trigger.STATE_ERROR:
+                    return "<span style='color:red'>Error</span>";
+                }
+                return "Unknown";
+            }
+        });
+        table.addColumn(statusColumn);
+
+        table.addColumn(new Column("interval"));
+
+        column = new Column("trigger.nextFireTime", "Next Run");
+        column.setFormat(DATE_FORMAT);
+        table.addColumn(column);
+
+        column = new Column("trigger.startTime", "First Run");
+        column.setFormat(DATE_FORMAT);
+        table.addColumn(column);
+
+        column = new Column("trigger.previousFireTime", "Last Run");
+        column.setFormat(DATE_FORMAT);
+        table.addColumn(column);
+
+        // Initialized action column links
+
+        editLink.setAttribute("class", "actionIcon");
+        editLink.setTitle("Edit Job");
+        addControl(editLink);
+
+        pauseLink.setAttribute("class", "actionIcon");
+        pauseLink.setTitle("Pause Job");
+        pauseLink.setActionListener(new ActionListener(){
+            public boolean onAction(Control source) {
+                String name = pauseLink.getValue();
+                getSchedulerService().pauseJob(name);
+                setFlashMessage("Paused job '" + name + "'");
+                return true;
+            }
+        });
+        addControl(pauseLink);
+
+        interruptLink.setAttribute("class", "actionIcon");
+        interruptLink.setTitle("Interrupt Running Job");
+        interruptLink.setActionListener(new ActionListener(){
+            public boolean onAction(Control source) {
+                String name = interruptLink.getValue();
+                if (getSchedulerService().interuptJob(name)) {
+                    setFlashMessage("Interrupted job '" + name + "'");
+                } else {
+                    setFlashMessage("Could not interrupt job '" + name + "'");
+                }
+                return true;
+            }
+        });
+        addControl(interruptLink);
+
+        triggerLink.setAttribute("class", "actionIcon");
+        triggerLink.setTitle("Trigger Job");
+        triggerLink.setActionListener(new ActionListener(){
+            public boolean onAction(Control source) {
+                String name = triggerLink.getValue();
+                getSchedulerService().triggerJob(name);
+                setFlashMessage("Triggered job '" + name +  "'");
+                return true;
+            }
+        });
+        addControl(triggerLink);
+
+        resumeLink.setAttribute("class", "actionIcon");
+        resumeLink.setTitle("Resume Job");
+        resumeLink.setActionListener(new ActionListener(){
+            public boolean onAction(Control source) {
+                String name = resumeLink.getValue();
+                getSchedulerService().resumeJob(name);
+                setFlashMessage("Resumed job '" + name + "'");
+                return true;
+            }
+        });
+        addControl(resumeLink);
+
+        deleteLink.setAttribute("class", "actionIcon");
+        deleteLink.setTitle("Delete Job");
+        String confirmMessage = getMessage("deleteConfirm", "Job");
+        deleteLink.setAttribute("onclick", "return window.confirm('" + confirmMessage + "')");
+        deleteLink.setActionListener(new ActionListener(){
+            public boolean onAction(Control source) {
+                String name = deleteLink.getValue();
+                if (getSchedulerService().deleteJob(name)) {
+                    setFlashMessage("Deleted job '" + name + "'");
+                } else {
+                    setFlashMessage("Could not delete " + name);
+                }
+                return true;
+            }
+        });
+        addControl(deleteLink);
+
+        // Add table action column if user has edit or delete permissions
+        final Column actionColumn = new Column("action");
+
+        // Render action controls based on users permission
+        actionColumn.setDecorator(new Decorator() {
+            public String render(Object object, Context context) {
+                JobAndSimpleTrigger jobAndTrigger = (JobAndSimpleTrigger) object;
+
+                HtmlStringBuffer buffer = new HtmlStringBuffer();
+
+                editLink.setParameter("job.name", jobAndTrigger.getJob().getName());
+                editLink.render(buffer);
+
+                buffer.append(" | ");
+                deleteLink.setValue(jobAndTrigger.getJob().getName());
+                deleteLink.render(buffer);
+
+                if (!getSchedulerService().isPaused()) {
+
+                    if (jobAndTrigger.getTriggerState() == Trigger.STATE_PAUSED) {
+                        buffer.append(" | ");
+                        resumeLink.setValue(jobAndTrigger.getJob().getName());
+                        resumeLink.render(buffer);
+
+                    } else {
+                        buffer.append(" | ");
+                        pauseLink.setValue(jobAndTrigger.getJob().getName());
+                        pauseLink.render(buffer);
+                    }
+
+                    buffer.append(" | ");
+                    triggerLink.setValue(jobAndTrigger.getJob().getName());
+                    triggerLink.render(buffer);
+
+                    if (jobAndTrigger.getTriggerState() == Trigger.STATE_BLOCKED) {
+                        buffer.append(" | ");
+                        interruptLink.setValue(jobAndTrigger.getJob().getName());
+                        interruptLink.render(buffer);
+                    }
+                }
+
+                return buffer.toString();
+            }
+
+        });
+        actionColumn.setSortable(false);
+        table.addColumn(actionColumn);
+
+        // Add Control Buttons.
+
+        newJob.setActionListener(new ActionListener(){
+            public boolean onAction(Control source) {
+                setRedirect(QuartzJobAndTriggerPage.class);
+                return false;
+            }
+        });
+        addControl(newJob);
+    }
+
+    // Event Handlers ---------------------------------------------------------
+
+    @Override
+    public void onInit() {
+        super.onInit();
+        if (getSchedulerService() == null) {
+            setFlashMessage(getMessage("jobSchedulerNotAvailableMsg"));
+            newJob.setDisabled(true);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public void onRender() {
+        super.onRender();
+
+        if (getSchedulerService() != null) {
+            List<JobAndSimpleTrigger> rowList = getSchedulerService().getJobAndTriggerList();
+            Collections.sort(rowList, new Ordering("job.name", Ordering.ASC));
+            table.setRowList(rowList);
+        }
+    }
+
+    // Protected Methods ------------------------------------------------------
+
+    /**
+     * Set a flash attribute message with the given message string.
+     *
+     * @param message the flash attribute message to display
+     */
+    protected void setFlashMessage(String message) {
+        getContext().setFlashAttribute("flash", message);
+    }
+
+    /**
+     * Return the scheduler service.
+     *
+     * @return the scheduler service
+     */
+    protected SchedulerService getSchedulerService() {
+        if (schedulerService == null) {
+            ServletContext servletContext = getContext().getServletContext();
+
+            StdSchedulerFactory schedulerFactory = (StdSchedulerFactory)
+                servletContext.getAttribute(QuartzInitializerListener.QUARTZ_FACTORY_KEY);
+
+            if (schedulerFactory != null) {
+                schedulerService = new SchedulerService(schedulerFactory);
+            }
+        }
+
+        return schedulerService;
+    }
+
+}

Added: incubator/click/trunk/click/examples/src/org/apache/click/examples/page/quartz/package.html
URL: http://svn.apache.org/viewvc/incubator/click/trunk/click/examples/src/org/apache/click/examples/page/quartz/package.html?rev=774364&view=auto
==============================================================================
--- incubator/click/trunk/click/examples/src/org/apache/click/examples/page/quartz/package.html (added)
+++ incubator/click/trunk/click/examples/src/org/apache/click/examples/page/quartz/package.html Wed May 13 13:45:15 2009
@@ -0,0 +1,22 @@
+<!--
+   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.
+-->
+
+<body>
+Provides the example Quartz scheduler integration page classes.
+</body>
\ No newline at end of file

Added: incubator/click/trunk/click/examples/src/org/apache/click/examples/quartz/ExampleJob.java
URL: http://svn.apache.org/viewvc/incubator/click/trunk/click/examples/src/org/apache/click/examples/quartz/ExampleJob.java?rev=774364&view=auto
==============================================================================
--- incubator/click/trunk/click/examples/src/org/apache/click/examples/quartz/ExampleJob.java (added)
+++ incubator/click/trunk/click/examples/src/org/apache/click/examples/quartz/ExampleJob.java Wed May 13 13:45:15 2009
@@ -0,0 +1,51 @@
+/*
+ * 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.click.examples.quartz;
+
+import org.quartz.Job;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+
+/**
+ * Provides a simple example Quartz Job class which writes 'Hello World' to
+ * System.out and then counts to 10.
+ *
+ * @author Malcolm Edgar
+ */
+public class ExampleJob implements Job {
+
+    /**
+     * {@link org.quartz.Job#execute(JobExecutionContext)
+     */
+    public void execute(JobExecutionContext context) throws JobExecutionException {
+        System.out.println("Hello World from " + getClass().getSimpleName() + "@" + hashCode());
+        System.out.println("I can count to 10.");
+
+        for (int i = 1; i <= 10; i++) {
+            System.out.print(i + " ");
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException ie) {
+            }
+        }
+
+        System.out.println("\nSee I did it.");
+    }
+
+}

Added: incubator/click/trunk/click/examples/src/org/apache/click/examples/quartz/JobAndSimpleTrigger.java
URL: http://svn.apache.org/viewvc/incubator/click/trunk/click/examples/src/org/apache/click/examples/quartz/JobAndSimpleTrigger.java?rev=774364&view=auto
==============================================================================
--- incubator/click/trunk/click/examples/src/org/apache/click/examples/quartz/JobAndSimpleTrigger.java (added)
+++ incubator/click/trunk/click/examples/src/org/apache/click/examples/quartz/JobAndSimpleTrigger.java Wed May 13 13:45:15 2009
@@ -0,0 +1,127 @@
+/*
+ * 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.click.examples.quartz;
+
+import java.util.Iterator;
+
+import org.quartz.JobDetail;
+import org.quartz.JobExecutionContext;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.quartz.SimpleTrigger;
+import org.quartz.Trigger;
+
+/**
+ * Provides a Quartz Job and Simple Trigger display class.
+ *
+ * @author Malcolm Edgar
+ */
+public class JobAndSimpleTrigger {
+
+    private static final long MS_TO_MINS = 1000L * 60;
+
+    private final JobDetail jobDetail;
+    private final SimpleTrigger trigger;
+    private final Scheduler scheduler;
+
+    public JobAndSimpleTrigger(JobDetail jobDetail, SimpleTrigger trigger, Scheduler scheduler) {
+        this.jobDetail = jobDetail;
+        this.trigger = trigger;
+        this.scheduler = scheduler;
+    }
+
+    public JobDetail getJob() {
+        return jobDetail;
+    }
+
+    public SimpleTrigger getTrigger() {
+        return trigger;
+    }
+
+    public int getTriggerState(){
+        try {
+            return scheduler.getTriggerState(trigger.getName(), trigger.getGroup());
+        } catch(Throwable th) {
+            return Trigger.STATE_NONE;
+        }
+    }
+
+    public String getTriggerStateAsString(){
+        switch(getTriggerState()){
+        case Trigger.STATE_NONE:
+            return "None";
+        case Trigger.STATE_NORMAL:
+            return "Normal";
+        case Trigger.STATE_PAUSED:
+            return "Paused";
+        case Trigger.STATE_BLOCKED:
+            return "Running";
+        case Trigger.STATE_COMPLETE:
+            return "Complete";
+        case Trigger.STATE_ERROR:
+            return "Error";
+        }
+        return "Unknown";
+    }
+
+    public String getRepeat() {
+        long count = trigger.getRepeatCount();
+
+        if (count == -1) {
+            return "Continously";
+
+        } else if (count == 0) {
+            return "Run once";
+
+        } else if (count == 1) {
+            return "Repeat once";
+
+        } else {
+            return "Repeat " + count;
+        }
+    }
+
+    public String getInterval() {
+        long interval = trigger.getRepeatInterval() / MS_TO_MINS;
+
+        if (interval == 1) {
+            return "1 min";
+
+        } else if (interval < 60) {
+            return "" + interval + " mins";
+
+        } else if (interval == 60) {
+            return "1 hour";
+
+        } else {
+            return "" + interval / 60 + " hours";
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public boolean isExecuting() throws SchedulerException {
+        for (Iterator i = scheduler.getCurrentlyExecutingJobs().iterator(); i.hasNext();) {
+            JobExecutionContext context = (JobExecutionContext) i.next();
+            if (context.getJobDetail().getFullName().equals(jobDetail.getFullName())) {
+                return true;
+            }
+        }
+        return false;
+    }
+}

Added: incubator/click/trunk/click/examples/src/org/apache/click/examples/quartz/SchedulerService.java
URL: http://svn.apache.org/viewvc/incubator/click/trunk/click/examples/src/org/apache/click/examples/quartz/SchedulerService.java?rev=774364&view=auto
==============================================================================
--- incubator/click/trunk/click/examples/src/org/apache/click/examples/quartz/SchedulerService.java (added)
+++ incubator/click/trunk/click/examples/src/org/apache/click/examples/quartz/SchedulerService.java Wed May 13 13:45:15 2009
@@ -0,0 +1,357 @@
+/*
+ * 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.click.examples.quartz;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.click.extras.cayenne.CayenneTemplate;
+import org.apache.commons.lang.Validate;
+import org.quartz.JobDetail;
+import org.quartz.JobExecutionContext;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.quartz.SimpleTrigger;
+import org.quartz.TriggerUtils;
+import org.quartz.impl.StdSchedulerFactory;
+
+/**
+ * Provides a service wrapper class around the Quartz scheduler class.
+ *
+ * @author Malcolm Edgar
+ */
+public class SchedulerService extends CayenneTemplate {
+
+    /** The scheduler group name. */
+    private static final String GROUP_NAME = "click-examples";
+
+    /** The scheduler trigger group name. */
+    private static final String TRIGGER_GROUP_NAME = "click-examples-trigger-group";
+
+    /** The quartz scheduler instance. */
+    private final Scheduler scheduler;
+
+    // Constructor ------------------------------------------------------------
+
+    /**
+     * Create a new scheduler service with the given Quartz Scheduler Factory.
+     *
+     * @param schedulerFactory the Quartz Scheduler Factory
+     */
+    public SchedulerService(StdSchedulerFactory schedulerFactory) {
+        Validate.notNull(schedulerFactory, "Null schedulerFactory param");
+
+        try {
+            scheduler = schedulerFactory.getScheduler();
+
+        } catch (SchedulerException se) {
+            String msg = "Could not obtain Quartz Scheduler instance";
+            throw new RuntimeException(msg);
+        }
+    }
+
+    // Public Methods ---------------------------------------------------------
+
+    /**
+     * Schedule a new quartz job with the given job details, start and end date,
+     * repeat count and repeat interval.
+     *
+     * @param jobDetail the quartz job detail
+     * @param startDate the start date of the job
+     * @param endDate the end date of the job
+     * @param repeatCount the repeat count
+     * @param repeatInterval the job repeat interval in milliseconds
+     */
+    public void scheduleJob(JobDetail jobDetail, Date startDate, Date endDate, int repeatCount, long repeatInterval) {
+        Validate.notNull(jobDetail, "Null jobDetail parameter");
+
+        // get a "nice round" time a few seconds in the future....
+        long ts = TriggerUtils.getNextGivenSecondDate(null, 10).getTime();
+
+        if (startDate == null || startDate.before(new Date())) {
+            startDate = new Date(ts);
+        }
+
+        jobDetail.setGroup(GROUP_NAME);
+
+        SimpleTrigger trigger =
+            new SimpleTrigger(jobDetail.getName(),
+                              TRIGGER_GROUP_NAME,
+                              jobDetail.getName(),
+                              jobDetail.getGroup(),
+                              startDate,
+                              endDate,
+                              repeatCount,
+                              repeatInterval);
+
+        try {
+            getScheduler().scheduleJob(jobDetail, trigger);
+
+        } catch (SchedulerException se) {
+            throw new RuntimeException( "Could not obtain schedule job " + jobDetail);
+        }
+    }
+
+    /**
+     * Return true if the scheduler is paused.
+     *
+     * @return true if the scheduler is paused
+     */
+    public boolean isPaused() {
+        try {
+            return getScheduler().isInStandbyMode();
+
+        } catch (SchedulerException se) {
+            throw new RuntimeException( "Could not determine if scheduler is in standby mode");
+        }
+    }
+
+    /**
+     * Pause all the scheduled jobs, and interrupt any currently executing jobs.
+     */
+    @SuppressWarnings("unchecked")
+    public void pauseAll() {
+        try {
+            for (Iterator i = scheduler.getCurrentlyExecutingJobs().iterator(); i.hasNext();) {
+                JobExecutionContext context = (JobExecutionContext) i.next();
+                interuptJob(context.getJobDetail().getName());
+            }
+
+            getScheduler().pauseAll();
+
+        } catch (SchedulerException se) {
+            String msg = "Could not pause all jobs";
+            throw new RuntimeException(msg, se);
+        }
+    }
+
+    /**
+     * Resume all paused jobs.
+     */
+    public void resumeAll() {
+        try {
+            getScheduler().resumeAll();
+
+        } catch (SchedulerException se) {
+            String msg = "Could not resume all jobs";
+            throw new RuntimeException(msg, se);
+        }
+    }
+
+    /**
+     * Pause the job for the given name.
+     *
+     * @param jobName the name of the job to pause
+     */
+    public void pauseJob(String jobName) {
+        try {
+            getScheduler().pauseJob(jobName, GROUP_NAME);
+
+        } catch (SchedulerException se) {
+            String msg = "Could not pause Quartz Scheduler job: " + jobName;
+            throw new RuntimeException(msg, se);
+        }
+    }
+
+    /**
+     * Resume the job for the given name.
+     *
+     * @param jobName the name of the job to resume
+     */
+    public void resumeJob(String jobName) {
+        try {
+            getScheduler().resumeJob(jobName, GROUP_NAME);
+
+        } catch (SchedulerException se) {
+            String msg = "Could not resume Quartz Scheduler job: " + jobName;
+            throw new RuntimeException(msg, se);
+        }
+    }
+
+    /**
+     * Trigger the job for the given name.
+     *
+     * @param jobName the name of the job to trigger
+     */
+    public void triggerJob(String jobName) {
+        try {
+            getScheduler().triggerJob(jobName, GROUP_NAME);
+
+        } catch (SchedulerException se) {
+            String msg = "Could not resume Quartz Scheduler job: " + jobName;
+            throw new RuntimeException(msg, se);
+        }
+    }
+
+    /**
+     * Interupt the job for the given name, return true if the job was found.
+     *
+     * @param jobName the name of the job to interupt
+     */
+    public boolean interuptJob(String jobName) {
+        try {
+            return getScheduler().interrupt(jobName, GROUP_NAME);
+
+        } catch (SchedulerException se) {
+            String msg = "Could not obtain Quartz Scheduler JobDetails";
+            throw new RuntimeException(msg, se);
+        }
+    }
+
+    /**
+     * Delete the job for the given name, return true if the job was found.
+     *
+     * @param jobName the name of the job to delete
+     * @return true if the Job was found and deleted.
+     */
+    public boolean deleteJob(String jobName) {
+        try {
+            return getScheduler().deleteJob(jobName, GROUP_NAME);
+
+        } catch (SchedulerException se) {
+            String msg = "Could not obtain Quartz Scheduler JobDetails";
+            throw new RuntimeException(msg, se);
+        }
+    }
+
+    /**
+     * Return the list of scheduled JobDetails for the group name: "click-group".
+     *
+     * @return the list of scheduled JobDetails for the group name: "click-group"
+     */
+    public List<JobDetail> getJobDetailList() {
+        try {
+            List<JobDetail> list = new ArrayList<JobDetail>();
+
+            String[] jobNames = getScheduler().getJobNames(GROUP_NAME);
+
+            for (int i = 0; i < jobNames.length; i++) {
+                list.add(getScheduler().getJobDetail(jobNames[i], GROUP_NAME));
+            }
+
+            return list;
+
+        } catch (SchedulerException se) {
+            String msg = "Could not obtain Quartz Scheduler JobDetails";
+            throw new RuntimeException(msg, se);
+        }
+    }
+
+    /**
+     * Return the JobDetail for the given name and group name: "click-group".
+     *
+     * @return the JobDetail for the given name and group name: "click-group".
+     */
+    public JobDetail getJobDetail(String jobName) {
+        try {
+            return getScheduler().getJobDetail(jobName, GROUP_NAME);
+
+        } catch (SchedulerException se) {
+            String msg = "Could not obtain Quartz Scheduler JobDetail";
+            throw new RuntimeException(msg, se);
+        }
+    }
+
+    /**
+     * Return true if the scheduler has the named job.
+     *
+     * @param jobName the name of the job
+     * @return true if the scheduler has the named job
+     */
+    public boolean hasJob(String jobName) {
+        try {
+            return (getScheduler().getJobDetail(jobName, GROUP_NAME) != null);
+
+        } catch (SchedulerException se) {
+            String msg = "Could not obtain Quartz Scheduler JobDetail";
+            throw new RuntimeException(msg, se);
+        }
+    }
+
+    /**
+     * Return the list of JobDetail and Trigger associations.
+     *
+     * @return the list of JobDetail and Trigger associations
+     */
+    public List<JobAndSimpleTrigger> getJobAndTriggerList() {
+        try {
+            List<JobAndSimpleTrigger> list = new ArrayList<JobAndSimpleTrigger>();
+
+            String[] jobNames = getScheduler().getJobNames(GROUP_NAME);
+
+            for (int i = 0; i < jobNames.length; i++) {
+                String jobName = jobNames[i];
+
+                JobDetail jobDetail = getJobDetail(jobName);
+
+                SimpleTrigger trigger  = (SimpleTrigger) getScheduler().getTrigger(jobName, TRIGGER_GROUP_NAME);
+
+                list.add(new JobAndSimpleTrigger(jobDetail, (SimpleTrigger) trigger, getScheduler()));
+            }
+
+            return list;
+
+        } catch (SchedulerException se) {
+            String msg = "Could not obtain Quartz Scheduler JobAndTriggerList";
+            throw new RuntimeException(msg, se);
+        }
+    }
+
+    /**
+     * Return the Job and Trigger for the given job name
+     *
+     * @param jobName the name of the job
+     * @return the Job and Trigger for the given job name
+     */
+    public JobAndSimpleTrigger getJobAndTrigger(String jobName) {
+        Validate.notNull(jobName, "Null jobName parameter");
+
+        try {
+            JobDetail jobDetail = getJobDetail(jobName);
+
+            if (jobDetail != null) {
+                SimpleTrigger trigger  = (SimpleTrigger)
+                    getScheduler().getTrigger(jobName, TRIGGER_GROUP_NAME);
+
+                return new JobAndSimpleTrigger(jobDetail, (SimpleTrigger) trigger, getScheduler());
+
+            } else {
+                return null;
+            }
+
+        } catch (SchedulerException se) {
+            String msg = "Could not obtain Quartz Scheduler JobAndTrigger";
+            throw new RuntimeException(msg, se);
+        }
+    }
+
+    // Private Methods --------------------------------------------------------
+
+    /**
+     * Return the schduler instance.
+     *
+     * @return the schduler instance
+     */
+    private Scheduler getScheduler() {
+        return scheduler;
+    }
+
+}

Added: incubator/click/trunk/click/examples/src/org/apache/click/examples/quartz/package.html
URL: http://svn.apache.org/viewvc/incubator/click/trunk/click/examples/src/org/apache/click/examples/quartz/package.html?rev=774364&view=auto
==============================================================================
--- incubator/click/trunk/click/examples/src/org/apache/click/examples/quartz/package.html (added)
+++ incubator/click/trunk/click/examples/src/org/apache/click/examples/quartz/package.html Wed May 13 13:45:15 2009
@@ -0,0 +1,22 @@
+<!--
+   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.
+-->
+
+<body>
+Provides the example Quartz scheduler integration classes.
+</body>
\ No newline at end of file

Copied: incubator/click/trunk/click/examples/src/org/apache/click/examples/util/DatabaseInitListener.java (from r769101, incubator/click/trunk/click/examples/src/org/apache/click/examples/util/DatabaseInitFilter.java)
URL: http://svn.apache.org/viewvc/incubator/click/trunk/click/examples/src/org/apache/click/examples/util/DatabaseInitListener.java?p2=incubator/click/trunk/click/examples/src/org/apache/click/examples/util/DatabaseInitListener.java&p1=incubator/click/trunk/click/examples/src/org/apache/click/examples/util/DatabaseInitFilter.java&r1=769101&r2=774364&rev=774364&view=diff
==============================================================================
--- incubator/click/trunk/click/examples/src/org/apache/click/examples/util/DatabaseInitFilter.java (original)
+++ incubator/click/trunk/click/examples/src/org/apache/click/examples/util/DatabaseInitListener.java Wed May 13 13:45:15 2009
@@ -30,18 +30,9 @@
 import java.util.Timer;
 import java.util.TimerTask;
 
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-
-import org.apache.click.examples.domain.Customer;
-import org.apache.click.examples.domain.PostCode;
-import org.apache.click.examples.domain.SystemCode;
-import org.apache.click.examples.domain.User;
-import org.apache.click.util.ClickUtils;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
 
 import org.apache.cayenne.access.DataContext;
 import org.apache.cayenne.access.DataDomain;
@@ -52,20 +43,31 @@
 import org.apache.cayenne.map.DataMap;
 import org.apache.cayenne.query.SelectQuery;
 import org.apache.click.examples.domain.Course;
+import org.apache.click.examples.domain.Customer;
+import org.apache.click.examples.domain.PostCode;
 import org.apache.click.examples.domain.StudentHouse;
+import org.apache.click.examples.domain.SystemCode;
+import org.apache.click.examples.domain.User;
+import org.apache.click.examples.quartz.ExampleJob;
+import org.apache.click.examples.quartz.SchedulerService;
+import org.apache.click.util.ClickUtils;
 import org.apache.commons.lang.WordUtils;
+import org.quartz.JobDetail;
+import org.quartz.ee.servlet.QuartzInitializerListener;
+import org.quartz.impl.StdSchedulerFactory;
 
 /**
- * Provides a database initialization filter. This servlet filter creates a
- * examples database schema using the Cayenne {@link DbGenerator} utility class,
- * and loads data files into the database.
+ * Provides a database initialization servlet context listener. This listener
+ * creates a examples database schema using the Cayenne {@link DbGenerator}
+ * utility class, and loads data files into the database. This class also
+ * adds an ExampleJob to the Quartz Scheduler.
  * <p/>
- * This filter also provides a customer reloading task which runs every 15
+ * This listener also provides a customer reloading task which runs every 15
  * minutes.
  *
  * @author Malcolm Edgar
  */
-public class DatabaseInitFilter implements Filter {
+public class DatabaseInitListener implements ServletContextListener {
 
     private static final long RELOAD_TIMER_INTERVAL = 1000 * 60 * 5;
 
@@ -76,10 +78,12 @@
     // --------------------------------------------------------- Public Methods
 
     /**
-     * @see Filter#init(javax.servlet.FilterConfig)
+     * @see ServletContextListener#contextInitialized(ServletContextEvent)
      */
-    public void init(FilterConfig config) throws ServletException {
-        ServletUtil.initializeSharedConfiguration(config.getServletContext());
+    public void contextInitialized(ServletContextEvent sce) {
+        ServletContext servletContext = sce.getServletContext();
+
+        ServletUtil.initializeSharedConfiguration(servletContext);
 
         try {
             DataDomain cayenneDomain =
@@ -91,29 +95,26 @@
 
             loadDatabase();
 
-            reloadTimer.schedule(new ReloadTask(), 10000, RELOAD_TIMER_INTERVAL);
+            StdSchedulerFactory schedulerFactory = (StdSchedulerFactory)
+                servletContext.getAttribute(QuartzInitializerListener.QUARTZ_FACTORY_KEY);
+
+            SchedulerService schedulerService = new SchedulerService(schedulerFactory);
+
+            loadQuartzJobs(schedulerService);
+
+            reloadTimer.schedule(new ReloadTask(schedulerService), 10000, RELOAD_TIMER_INTERVAL);
+
 
         } catch (Exception e) {
             e.printStackTrace();
-            throw new ServletException("Error creating database", e);
+            throw new RuntimeException("Error creating database", e);
         }
     }
 
- 
     /**
-     * @see Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
-     */
-    public void doFilter(ServletRequest request, ServletResponse response,
-            FilterChain filterChain) throws IOException, ServletException {
-        filterChain.doFilter(request, response);
-    }
-
-    /**
-     * Cancel the reload timer.
-     *
-     * @see Filter#destroy()
+     * @see ServletContextListener#contextDestroyed(ServletContextEvent)
      */
-    public void destroy() {
+    public void contextDestroyed(ServletContextEvent sce) {
         reloadTimer.cancel();
     }
 
@@ -172,7 +173,7 @@
     private static void loadFile(String filename, DataContext dataContext,
             LineProcessor lineProcessor) throws IOException {
 
-        InputStream is = ClickUtils.getResourceAsStream(filename, DatabaseInitFilter.class);
+        InputStream is = ClickUtils.getResourceAsStream(filename, DatabaseInitListener.class);
 
         if (is == null) {
             throw new RuntimeException("classpath file not found: " + filename);
@@ -311,10 +312,6 @@
         });
     }
 
-    private static interface LineProcessor {
-        public void processLine(String line, DataContext dataContext);
-    }
-
     private static String next(StringTokenizer tokenizer) {
         String token = tokenizer.nextToken().trim();
         if (token.startsWith("\"")) {
@@ -334,15 +331,45 @@
         }
     }
 
+    private static void loadQuartzJobs(SchedulerService schedulerService) {
+
+        // Create Submission Synchronize Job
+        if (!schedulerService.hasJob(ExampleJob.class.getSimpleName())) {
+            JobDetail jobDetail = new JobDetail();
+
+            jobDetail.setName(ExampleJob.class.getSimpleName());
+            jobDetail.setDescription("Demonstration job write Hello World");
+            jobDetail.setJobClass(ExampleJob.class);
+
+            // 5 minute interval
+            final long fiveMinutesInMs = 24 * 60 * 60 * 1000;
+
+            schedulerService.scheduleJob(jobDetail, new Date(), null, -1, fiveMinutesInMs);
+        }
+    }
+
+    // Inner Classes ----------------------------------------------------------
+
+    private static interface LineProcessor {
+        public void processLine(String line, DataContext dataContext);
+    }
+
     private static class ReloadTask extends TimerTask {
 
+        private SchedulerService schedulerService;
+
+        public ReloadTask(SchedulerService schedulerService) {
+            this.schedulerService = schedulerService;
+        }
+
+        @SuppressWarnings("unchecked")
         public void run() {
             DataContext dataContext = null;
             try {
                 dataContext = DataContext.createDataContext();
 
                 SelectQuery query = new SelectQuery(Customer.class);
-                List list = dataContext.performQuery(query);
+                List<Customer> list = dataContext.performQuery(query);
 
                 if (list.size() < 60) {
                     dataContext.deleteObjects(list);
@@ -352,6 +379,8 @@
                     dataContext.commitChanges();
                 }
 
+                loadQuartzJobs(schedulerService);
+
             } catch (Throwable t) {
                 t.printStackTrace();
 
@@ -360,6 +389,5 @@
                 }
             }
         }
-
     }
 }

Added: incubator/click/trunk/click/examples/src/quartz.properties
URL: http://svn.apache.org/viewvc/incubator/click/trunk/click/examples/src/quartz.properties?rev=774364&view=auto
==============================================================================
--- incubator/click/trunk/click/examples/src/quartz.properties (added)
+++ incubator/click/trunk/click/examples/src/quartz.properties Wed May 13 13:45:15 2009
@@ -0,0 +1,9 @@
+#
+# Quartz Scheduler configuration properties
+#
+org.quartz.scheduler.instanceName = DefaultQuartzScheduler
+org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
+org.quartz.threadPool.threadCount = 5
+org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore
+org.quartz.jobStore.misfireThreshold = 60000
+

Propchange: incubator/click/trunk/click/examples/webapp/WEB-INF/lib/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Wed May 13 13:45:15 2009
@@ -33,3 +33,4 @@
 geronimo-annotation_1.0_spec-1.1.1.jar
 spring-2.5.6.jar
 acegi-security-1.0.7.jar
+quartz-all-1.6.3.jar

Modified: incubator/click/trunk/click/examples/webapp/WEB-INF/menu.xml
URL: http://svn.apache.org/viewvc/incubator/click/trunk/click/examples/webapp/WEB-INF/menu.xml?rev=774364&r1=774363&r2=774364&view=diff
==============================================================================
--- incubator/click/trunk/click/examples/webapp/WEB-INF/menu.xml (original)
+++ incubator/click/trunk/click/examples/webapp/WEB-INF/menu.xml Wed May 13 13:45:15 2009
@@ -139,10 +139,12 @@
     <menu separator="true"/>
     <menu label="Apache Cayenne Form" path="cayenne/cayenne-form-page.htm"/>
     <menu label="Cayenne Tabbed Form" path="cayenne/tabbed-cayenne-form-page.htm"/>
-    <menu label="Accommodation Demo &lt;br&gt; (one-to-many)" path="cayenne/accommodation-demo.htm"/>
-    <menu label="Enrollment Demo &lt;br&gt; (many-to-many)" path="cayenne/enrollment-demo.htm"/>
+    <menu label="PropertySelect Demo (1-to-*)" path="cayenne/accommodation-demo.htm"/>
+    <menu label="PickList Demo (*-to-*)" path="cayenne/enrollment-demo.htm"/>
     <menu separator="true"/>
     <menu label="Apache POI Excel Export" path="general/excel-export.htm"/>
+    <menu separator="true"/>
+    <menu label="Quartz Scheduler" path="quartz/quartz-job-scheduler.htm"/>
   </menu>
 
 </menu>

Modified: incubator/click/trunk/click/examples/webapp/WEB-INF/web.xml
URL: http://svn.apache.org/viewvc/incubator/click/trunk/click/examples/webapp/WEB-INF/web.xml?rev=774364&r1=774363&r2=774364&view=diff
==============================================================================
--- incubator/click/trunk/click/examples/webapp/WEB-INF/web.xml (original)
+++ incubator/click/trunk/click/examples/webapp/WEB-INF/web.xml Wed May 13 13:45:15 2009
@@ -1,156 +1,165 @@
-<?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.
--->
-
-<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
-    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
-    version="2.4">
-    
-	<display-name>Click Examples</display-name>
-	
-	<!-- 
-	Spring configuration location. 
-	-->
-	<context-param>
-		<param-name>contextConfigLocation</param-name>
-		<param-value>WEB-INF/spring-beans.xml</param-value>
-	</context-param>
-
-	<!-- 
-	Provides an in memory databae initialization filter. In a production 
-	application a separate database would be used, and this filter would not
-	be needed.
-	-->
-	<filter>
-		<filter-name>DatabaseInitFilter</filter-name>
-		<filter-class>org.apache.click.examples.util.DatabaseInitFilter</filter-class>
-	</filter>
- 
-	<!--
-	Provides a thread local Cayenne DataContext filter.
-	-->
-	<filter>
-		<filter-name>DataContextFilter</filter-name>
-		<filter-class>org.apache.click.extras.cayenne.DataContextFilter</filter-class>
-		<init-param>
-			<param-name>session-scope</param-name>
- 			<param-value>false</param-value>
-		</init-param>
-	</filter>
- 
-	<!-- 
-	Provides a web application performance filter which compresses the response
-	and sets the Expires header on selected static resources. 
-	The "cachable-paths" init parameter tells the filter resources can have 
-	their Expires header set so the browser will cache them.
-	The "excludes-path" init parameter tells the filter which requests should
-	be ignored by the filter.
-	-->
-	<filter>
-		<filter-name>PerformanceFilter</filter-name>
-		<filter-class>org.apache.click.extras.filter.PerformanceFilter</filter-class>
-		<init-param>
-			<param-name>cachable-paths</param-name>
- 			<param-value>/assets/*</param-value>
-		</init-param>
-		<init-param>
-			<param-name>exclude-paths</param-name>
- 			<param-value>*/excel-export.htm</param-value>
-		</init-param>
-	</filter>
-	
-	<!-- 
-	Provides a ACEGI Security filter. 
-	-->
-	<filter>
-		<filter-name>FilterToBeanProxy</filter-name>
-		<filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
-		<init-param>
-			<param-name>targetClass</param-name>
-			<param-value>org.acegisecurity.util.FilterChainProxy</param-value>
-		</init-param>
-	</filter>
-		
-	<filter-mapping>
-		<filter-name>FilterToBeanProxy</filter-name>
-		<url-pattern>/*</url-pattern>
-	</filter-mapping>
-
- 	<filter-mapping>
-		<filter-name>DatabaseInitFilter</filter-name>
-		<servlet-name>ClickServlet</servlet-name>
-	</filter-mapping>
-
-	<filter-mapping>
-		<filter-name>DataContextFilter</filter-name>
-		<servlet-name>ClickServlet</servlet-name>
-	</filter-mapping>
-
-	<filter-mapping>
-		<filter-name>PerformanceFilter</filter-name>
-		<servlet-name>ClickServlet</servlet-name>
-	</filter-mapping>
-
-	<filter-mapping>
-		<filter-name>PerformanceFilter</filter-name>
-		<url-pattern>*.css</url-pattern>
-	</filter-mapping>
-	
-	<filter-mapping>
-		<filter-name>PerformanceFilter</filter-name>
-		<url-pattern>*.js</url-pattern>
-	</filter-mapping>
-	
-	<filter-mapping>
-		<filter-name>PerformanceFilter</filter-name>
-		<url-pattern>*.gif</url-pattern>
-	</filter-mapping>
-	
-	<filter-mapping>
-		<filter-name>PerformanceFilter</filter-name>
-		<url-pattern>*.png</url-pattern>
-	</filter-mapping>
-	
-	<!-- 
-	The Spring Context Loader which initializes the Spring runtime. 
-	-->
-	<listener>
-		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
-	</listener>
-
-	<!--
-	The Spring Click Servlet which handles *.htm requests.
-	-->
-	<servlet>
-		<servlet-name>ClickServlet</servlet-name>
-		<servlet-class>org.apache.click.extras.spring.SpringClickServlet</servlet-class>
-		<load-on-startup>0</load-on-startup>
-	</servlet>
-
-	<servlet-mapping>
-		<servlet-name>ClickServlet</servlet-name>
-		<url-pattern>*.htm</url-pattern>
-	</servlet-mapping>
-
-	<welcome-file-list>
-		<welcome-file>redirect.html</welcome-file>
-	</welcome-file-list>
-
-</web-app>
+<?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.
+-->
+
+<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
+    version="2.4">
+    
+	<display-name>Click Examples</display-name>
+	
+	<!-- 
+	Spring configuration location. 
+	-->
+	<context-param>
+		<param-name>contextConfigLocation</param-name>
+		<param-value>WEB-INF/spring-beans.xml</param-value>
+	</context-param>
+	
+	<!-- Filters -->
+
+	<!--
+	Provides a thread local Cayenne DataContext filter.
+	-->
+	<filter>
+		<filter-name>DataContextFilter</filter-name>
+		<filter-class>org.apache.click.extras.cayenne.DataContextFilter</filter-class>
+		<init-param>
+			<param-name>session-scope</param-name>
+ 			<param-value>false</param-value>
+		</init-param>
+	</filter>
+ 
+	<!-- 
+	Provides a web application performance filter which compresses the response
+	and sets the Expires header on selected static resources. 
+	The "cachable-paths" init parameter tells the filter resources can have 
+	their Expires header set so the browser will cache them.
+	The "excludes-path" init parameter tells the filter which requests should
+	be ignored by the filter.
+	-->
+	<filter>
+		<filter-name>PerformanceFilter</filter-name>
+		<filter-class>org.apache.click.extras.filter.PerformanceFilter</filter-class>
+		<init-param>
+			<param-name>cachable-paths</param-name>
+ 			<param-value>/assets/*</param-value>
+		</init-param>
+		<init-param>
+			<param-name>exclude-paths</param-name>
+ 			<param-value>*/excel-export.htm</param-value>
+		</init-param>
+	</filter>
+	
+	<!-- 
+	Provides a ACEGI Security filter. 
+	-->
+	<filter>
+		<filter-name>FilterToBeanProxy</filter-name>
+		<filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
+		<init-param>
+			<param-name>targetClass</param-name>
+			<param-value>org.acegisecurity.util.FilterChainProxy</param-value>
+		</init-param>
+	</filter>
+		
+	<filter-mapping>
+		<filter-name>FilterToBeanProxy</filter-name>
+		<url-pattern>/*</url-pattern>
+	</filter-mapping>
+
+	<filter-mapping>
+		<filter-name>DataContextFilter</filter-name>
+		<servlet-name>ClickServlet</servlet-name>
+	</filter-mapping>
+
+	<filter-mapping>
+		<filter-name>PerformanceFilter</filter-name>
+		<servlet-name>ClickServlet</servlet-name>
+	</filter-mapping>
+
+	<filter-mapping>
+		<filter-name>PerformanceFilter</filter-name>
+		<url-pattern>*.css</url-pattern>
+	</filter-mapping>
+	
+	<filter-mapping>
+		<filter-name>PerformanceFilter</filter-name>
+		<url-pattern>*.js</url-pattern>
+	</filter-mapping>
+	
+	<filter-mapping>
+		<filter-name>PerformanceFilter</filter-name>
+		<url-pattern>*.gif</url-pattern>
+	</filter-mapping>
+	
+	<filter-mapping>
+		<filter-name>PerformanceFilter</filter-name>
+		<url-pattern>*.png</url-pattern>
+	</filter-mapping>
+		
+	<!-- Listeners -->
+	
+	<!--  
+	The Quartz initialization listenger which loads the Quartz scheduler.  
+	-->
+	<listener>
+		<listener-class>org.quartz.ee.servlet.QuartzInitializerListener</listener-class>
+	</listener>
+	
+	<!-- 
+	The Spring Context Loader which initializes the Spring runtime. 
+	-->
+	<listener>
+		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+	</listener>
+	
+	<!-- 
+	Provides an in memory databae initialization listener. In a production 
+	application a separate database would be used, and this listener would not
+	be needed.
+	-->
+	<listener>
+		<listener-class>org.apache.click.examples.util.DatabaseInitListener</listener-class>
+	</listener>
+		
+	<!-- Servlets -->
+
+	<!--
+	The Spring Click Servlet which handles *.htm requests.
+	-->
+	<servlet>
+		<servlet-name>ClickServlet</servlet-name>
+		<servlet-class>org.apache.click.extras.spring.SpringClickServlet</servlet-class>
+		<load-on-startup>0</load-on-startup>
+	</servlet>
+
+	<servlet-mapping>
+		<servlet-name>ClickServlet</servlet-name>
+		<url-pattern>*.htm</url-pattern>
+	</servlet-mapping>
+	
+	<!-- Wecome Files -->
+
+	<welcome-file-list>
+		<welcome-file>redirect.html</welcome-file>
+	</welcome-file-list>
+
+</web-app>

Added: incubator/click/trunk/click/examples/webapp/quartz/quartz-job-and-trigger.htm
URL: http://svn.apache.org/viewvc/incubator/click/trunk/click/examples/webapp/quartz/quartz-job-and-trigger.htm?rev=774364&view=auto
==============================================================================
--- incubator/click/trunk/click/examples/webapp/quartz/quartz-job-and-trigger.htm (added)
+++ incubator/click/trunk/click/examples/webapp/quartz/quartz-job-and-trigger.htm Wed May 13 13:45:15 2009
@@ -0,0 +1,8 @@
+#set ($flash = "") 
+#set ($flash = $session.flash) 
+#if ($flash != "")
+  <b>Flash Message:</b>
+  <p class="infoMsg">$flash</p>
+#end
+
+$form

Added: incubator/click/trunk/click/examples/webapp/quartz/quartz-job-scheduler.htm
URL: http://svn.apache.org/viewvc/incubator/click/trunk/click/examples/webapp/quartz/quartz-job-scheduler.htm?rev=774364&view=auto
==============================================================================
--- incubator/click/trunk/click/examples/webapp/quartz/quartz-job-scheduler.htm (added)
+++ incubator/click/trunk/click/examples/webapp/quartz/quartz-job-scheduler.htm Wed May 13 13:45:15 2009
@@ -0,0 +1,24 @@
+
+<p>
+This example shows you how to integrate the 
+<a -target="blank" href="http://www.opensymphony.com/quartz/">Quartz</a>
+job scheduling framework into a Click application. The 
+<a target="blank" href="http://localhost:9090/click-examples/source-viewer.htm?filename=WEB-INF/classes/org/apache/click/examples/quartz/ExampleJob.java">ExampleJob</a> 
+below was added to Quartz by the Click Examples DatabaseInitListener.
+</p>
+
+#set ($flash = "") 
+#set ($flash = $session.flash) 
+#if ($flash != "")
+  <p class="infoMsg">$flash</p>
+#end
+
+$table
+
+## Render the control buttons
+<div style="margin-top:0.75em;margin-left:0.5em;">
+$refresh 
+#if ($pauseAllJobs) $pauseAllJobs #end
+#if ($resumeAllJobs) $resumeAllJobs #end
+#if ($newJob) $newJob #end
+</div>