[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