[Yanel-commits] rev 27697 - in public/yanel/trunk/src/impl/java/org/wyona/yanel/impl/resources/usecase: . thread

simon at wyona.com simon at wyona.com
Thu Sep 27 09:46:57 CEST 2007


Author: simon
Date: 2007-09-27 09:46:56 +0200 (Thu, 27 Sep 2007)
New Revision: 27697

Added:
   public/yanel/trunk/src/impl/java/org/wyona/yanel/impl/resources/usecase/thread/
   public/yanel/trunk/src/impl/java/org/wyona/yanel/impl/resources/usecase/thread/ThreadUsecaseResource.java
   public/yanel/trunk/src/impl/java/org/wyona/yanel/impl/resources/usecase/thread/UsecaseThread.java
Log:
support background threads for usecases added. see bug#5575. thanks to josias!

Added: public/yanel/trunk/src/impl/java/org/wyona/yanel/impl/resources/usecase/thread/ThreadUsecaseResource.java
===================================================================
--- public/yanel/trunk/src/impl/java/org/wyona/yanel/impl/resources/usecase/thread/ThreadUsecaseResource.java	                        (rev 0)
+++ public/yanel/trunk/src/impl/java/org/wyona/yanel/impl/resources/usecase/thread/ThreadUsecaseResource.java	2007-09-27 07:46:56 UTC (rev 27697)
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2007 Wyona
+ */
+
+package org.wyona.yanel.impl.resources.usecase.thread;
+
+import javax.servlet.http.HttpSession;
+
+import org.apache.log4j.Category;
+import org.wyona.yanel.impl.resources.usecase.ExecutableUsecaseResource;
+import org.wyona.yanel.impl.resources.usecase.UsecaseException;
+import org.wyona.yanel.impl.resources.usecase.UsecaseView;
+
+/**
+ * A resource which executes a background thread and shows periodical status information.
+ * It has four views:
+ *   default (lets the user choose some parameters and has a button to start the thread)
+ *   status (periodically shows the progress of the thread and has a cancel button)
+ *   done (shows the result when the thread has finished)
+ *   cancel (displayed when the user hits cancel)
+ */
+public abstract class ThreadUsecaseResource extends ExecutableUsecaseResource {
+    
+    private static Category log = Category.getInstance(ThreadUsecaseResource.class);
+
+    /**
+     * @see org.wyona.yanel.impl.resources.usecase.UsecaseResource#processUsecase(java.lang.String)
+     */
+    protected UsecaseView processUsecase(String viewID) throws UsecaseException {
+        UsecaseView view = null;
+        try {
+            HttpSession session = getEnvironment().getRequest().getSession();
+           
+            UsecaseThread thread = getThread();
+           
+            if (thread == null) {
+                if (getParameter("start") != null && checkPreconditions() && !hasErrors()) {
+                    if (log.isDebugEnabled()) {
+                        log.debug("starting thread: " + getThreadID());
+                    }
+                    // start new thread:
+                    execute();
+                    // show status
+                    view = generateView("status");
+                } else if (getParameter("cancel") != null) {
+                    // user hit cancel after the thread has finished
+                    view = generateView("cancel");
+                } else {
+                    // show default view
+                    view = generateView("default");
+                }
+            } else {
+                // thread has already been started
+                if (getParameter("cancel") != null) {
+                    // user cancelled the generation
+                    if (log.isDebugEnabled()) {
+                        log.debug("cancelling thread: " + getThreadID());
+                    }
+                    cancel();
+                    view = generateView("cancel");
+                    
+                } else if (thread.isDone()) {
+                    // thread has finished
+                    
+                    if (log.isDebugEnabled()) {
+                        log.debug("finished thread: " + getThreadID());
+                    }
+                    view = generateView("done");
+                    //view = getThreadResultView(thread);
+                    thread.detachThreadFromSession(session);
+                    
+                } else {
+                    // show status
+                    view = generateView("status");
+                }
+            }
+        } catch (Exception e) {
+            log.error(e, e);
+            return null;
+        }
+        return view;
+    }
+
+    /**
+     * Creates and starts the thread and attaches it to the session.
+     * @throws UsecaseException
+     */
+    public void execute() throws UsecaseException {
+        HttpSession session = getEnvironment().getRequest().getSession();
+        UsecaseThread thread = createThread();
+        thread.attachThreadToSession(session);
+        thread.start();
+    }
+
+    /**
+     * Cancels the thread and removes it from the session.
+     * @throws UsecaseException
+     */
+    public void cancel() throws UsecaseException {
+        HttpSession session = getEnvironment().getRequest().getSession();
+        UsecaseThread thread = getThread(); 
+        thread.cancel();
+        thread.detachThreadFromSession(session);
+    }
+    
+    /**
+     * Creates the thread which will be executed by this resource.
+     * Implement this method in a subclass.
+     * @return thread
+     */
+    public abstract UsecaseThread createThread();
+
+    /**
+     * Gets an id which identifies this thread.
+     * @return id
+     */
+    public abstract String getThreadID();
+    
+    /**
+     * Gets the thread from the session.
+     * This method is also called by the jelly template.
+     * @return thread or null if the thread is not attached to the session. 
+     */
+    public UsecaseThread getThread() {
+        HttpSession session = getEnvironment().getRequest().getSession();
+        return UsecaseThread.getThreadFromSession(session, getThreadID());
+    }
+
+    /**
+     * @see org.wyona.yanel.impl.resources.usecase.UsecaseResource#exists()
+     */
+    public boolean exists() throws Exception {
+        return true;
+    }
+
+    /**
+     * @see org.wyona.yanel.impl.resources.usecase.UsecaseResource#getSize()
+     */
+    public long getSize() throws Exception {
+        // TODO: not implemented yet
+        log.warn("TODO: Method is not implemented yet");
+        return -1;
+    }
+
+    /**
+     * @see org.wyona.yanel.impl.resources.usecase.UsecaseResource#getMimeType(java.lang.String)
+     */
+    public String getMimeType(String viewId) throws Exception {
+        return "application/xml";
+    }
+}

Added: public/yanel/trunk/src/impl/java/org/wyona/yanel/impl/resources/usecase/thread/UsecaseThread.java
===================================================================
--- public/yanel/trunk/src/impl/java/org/wyona/yanel/impl/resources/usecase/thread/UsecaseThread.java	                        (rev 0)
+++ public/yanel/trunk/src/impl/java/org/wyona/yanel/impl/resources/usecase/thread/UsecaseThread.java	2007-09-27 07:46:56 UTC (rev 27697)
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2007 Wyona
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.wyona.org/licenses/APACHE-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.wyona.yanel.impl.resources.usecase.thread;
+
+import java.io.Serializable;
+
+import javax.servlet.http.HttpSession;
+
+import org.apache.log4j.Category;
+
+/**
+ * A thread which can be executed by the ThreadUsecaseResource.
+ */
+public abstract class UsecaseThread extends Thread implements Serializable {
+
+    private static Category log = Category.getInstance(UsecaseThread.class);
+
+    protected String threadID;
+    protected int progress = 0;
+    protected boolean cancelled = false;
+    protected boolean done = false;
+    protected StringBuffer eventLog;
+    
+    public UsecaseThread(String threadID) {
+        this.threadID = threadID;
+        this.eventLog = new StringBuffer();
+    }
+
+    /**
+     * Attaches this thread to the given session.
+     * @param session
+     * @throws IllegalStateException if a thread with the same key already exists in this session.
+     */
+    public void attachThreadToSession(HttpSession session) throws IllegalStateException {
+        String attrName = getThreadKey(this.threadID);
+        if (session.getAttribute(attrName) != null) {
+            String errorMsg = "thread exists already";
+            log.error(errorMsg);
+            throw new IllegalStateException(errorMsg);
+        }
+        session.setAttribute(attrName, this);
+    }
+    
+    /**
+     * Creates the key for this thread which is used to store and identify the thread in the session.
+     * @param threadID
+     * @return
+     */
+    protected static String getThreadKey(String threadID) {
+        return "yanel.thread." + threadID;
+    }
+    
+    /**
+     * Gets the thread with the given id from the session.
+     * @param session
+     * @param threadID
+     * @return thread or null if there is no such thread attached to the session
+     */
+    public static UsecaseThread getThreadFromSession(HttpSession session, String threadID) {
+        String attrName = getThreadKey(threadID);
+        return (UsecaseThread)session.getAttribute(attrName); 
+    }
+    
+    /**
+     * Removes this thread from the given session.
+     * If this thread does not exist in this session, this method does nothing.
+     * @param session
+     */
+    public void detachThreadFromSession(HttpSession session) {
+        String attrName = getThreadKey(this.threadID);
+        session.removeAttribute(attrName);
+    }
+    
+    /**
+     * Gets a progress indicator to show the progress of this thread.
+     * It must be a number in the range from 0 to 100.
+     * The returned value is only informational.
+     * @return progress
+     */
+    public int getProgress() {
+        return this.progress;
+    }
+    
+    /**
+     * Gets an informational string about the progress of this thread.
+     * @return log
+     */
+    public String getLog() {
+        return this.eventLog.toString();
+    }
+    
+    /**
+     * Indicates to the thread that it should be cancelled.
+     * Note that it is the responsibility of the thread to check peridically if
+     * it has been cancelled, and therefore should abort itself.
+     */
+    public void cancel() {
+        this.cancelled = true;
+    }
+    
+    /**
+     * Indicates whether this thread has been cancelled.
+     * @return true if this thread has been cancelled.
+     */
+    public boolean isCancelled() {
+        return this.cancelled;
+    }
+    
+    /**
+     * Indicates whether this thread has finished.
+     * @return true if finished.
+     */
+    public boolean isDone() {
+        return this.done;
+    }
+
+    /**
+     * Calls the doRun() method and sets the done flag to true afterwards.
+     * @see java.lang.Thread#run()
+     */
+    public void run() {
+        try {
+            doRun();
+        } catch (Exception e) {
+            log.error(e, e);
+            throw new RuntimeException(e.getMessage(), e);
+        } finally {
+            this.done = true;
+        }
+    }
+    
+    /**
+     * Implement this method in a subclass and put the actual code
+     * of this thread there. This method should periodically check for the
+     * cancel flag. Optionally it may periodically increase the value of the 
+     * progress field. 
+     * @throws Exception
+     */
+    public abstract void doRun() throws Exception;
+
+}



More information about the Yanel-commits mailing list