001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software GmbH & Co. KG, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.workplace.commons; 029 030import org.opencms.jsp.CmsJspActionElement; 031import org.opencms.main.CmsException; 032import org.opencms.main.CmsIllegalArgumentException; 033import org.opencms.main.CmsIllegalStateException; 034import org.opencms.main.CmsLog; 035import org.opencms.util.CmsStringUtil; 036import org.opencms.util.CmsUUID; 037import org.opencms.workplace.CmsWorkplace; 038import org.opencms.workplace.list.A_CmsListDialog; 039 040import java.util.HashMap; 041import java.util.Iterator; 042import java.util.Map; 043 044import javax.servlet.http.HttpServletRequest; 045import javax.servlet.http.HttpServletResponse; 046import javax.servlet.jsp.PageContext; 047 048import org.apache.commons.logging.Log; 049 050/** 051 * This is a widget to be used in a dialog which should show a progress bar based 052 * on a list.<p> 053 * 054 * The progress bar uses Ajax to not reload the whole page. The code which runs 055 * inside the thread has to update the progress in the current thread.<p> 056 * 057 * The progress to be displayed is the progress of building large lists which can 058 * take some time until they are finished.<p> 059 * 060 * There is a progress bar shown with the percentages on the left. Additionaly it 061 * is possible to show a description above the progress bar.<p> 062 * 063 * @see A_CmsListDialog 064 * 065 * @since 7.0.0 066 */ 067public class CmsProgressWidget { 068 069 /** The name of the key request parameter. */ 070 public static final String PARAMETER_KEY = "progresskey"; 071 072 /** The name of the refresh rate request parameter. */ 073 public static final String PARAMETER_REFRESHRATE = "refreshrate"; 074 075 /** The name of the show wait time request parameter. */ 076 public static final String PARAMETER_SHOWWAITTIME = "showwaittime"; 077 078 /** The time period after finished thread will be removed (10 min). */ 079 private static final long CLEANUP_PERIOD = 10 * 60 * 1000; 080 081 /** The default width of the progress bar. */ 082 private static final String DEFAULT_COLOR = "blue"; 083 084 /** The default refresh rate (in ms) of the progress bar. */ 085 private static final int DEFAULT_REFRESH_RATE = 2000; 086 087 /** The default width of the progress bar. */ 088 private static final String DEFAULT_WIDTH = "200px"; 089 090 /** The log object for this class. */ 091 private static final Log LOG = CmsLog.getLog(CmsProgressWidget.class); 092 093 /** Map of running threads. */ 094 private static Map<String, CmsProgressThread> m_threads = new HashMap<String, CmsProgressThread>(); 095 096 /** The color of the progress bar. */ 097 private String m_color; 098 099 /** The name of the JavaScript method to call after progress is finished. */ 100 private String m_jsFinishMethod; 101 102 /** The current JSP action element. */ 103 private CmsJspActionElement m_jsp; 104 105 /** The unique key of the thread belonging to this widget. */ 106 private String m_key; 107 108 /** The time interval the progress gets refreshed (in ms). */ 109 private int m_refreshRate; 110 111 /** The time period the show the wait symbol before the progress bar is shown.<p> 112 * Set to 0 (zero) to disable this.<p> */ 113 private int m_showWaitTime; 114 115 /** The width of the progress bar to use in HTML.<p> */ 116 private String m_width; 117 118 /** 119 * Public constructor.<p> 120 * 121 * @param jsp an initialized JSP action element 122 */ 123 public CmsProgressWidget(CmsJspActionElement jsp) { 124 125 m_jsp = jsp; 126 127 // set default values 128 m_width = DEFAULT_WIDTH; 129 m_color = DEFAULT_COLOR; 130 131 // find the show wait time from the request 132 m_showWaitTime = 0; 133 if (getJsp().getRequest().getParameter(PARAMETER_SHOWWAITTIME) != null) { 134 m_showWaitTime = Integer.valueOf(getJsp().getRequest().getParameter(PARAMETER_SHOWWAITTIME)).intValue(); 135 } 136 137 // find the show wait time from the request 138 m_refreshRate = DEFAULT_REFRESH_RATE; 139 if (getJsp().getRequest().getParameter(PARAMETER_REFRESHRATE) != null) { 140 m_refreshRate = Integer.valueOf(getJsp().getRequest().getParameter(PARAMETER_REFRESHRATE)).intValue(); 141 } 142 143 // find the key from the request 144 m_key = getJsp().getRequest().getParameter(PARAMETER_KEY); 145 if (m_key == null) { 146 // generate unique key 147 m_key = new CmsUUID().toString(); 148 } 149 } 150 151 /** 152 * Public constructor with JSP variables.<p> 153 * 154 * @param context the JSP page context 155 * @param req the JSP request 156 * @param res the JSP response 157 */ 158 public CmsProgressWidget(PageContext context, HttpServletRequest req, HttpServletResponse res) { 159 160 this(new CmsJspActionElement(context, req, res)); 161 } 162 163 /** 164 * Returns the thread for the progress with the given key.<p> 165 * 166 * @param key the key of the thread 167 * 168 * @return the thread for the progress with the given key 169 */ 170 public static CmsProgressThread getProgressThread(String key) { 171 172 return m_threads.get(key); 173 } 174 175 /** 176 * Removes the thread for the progress with the given key from the list with the actual threads.<p> 177 * 178 * @param key the key of the thread for the progress to remove from the list 179 */ 180 public static void removeProgressThread(String key) { 181 182 m_threads.remove(key); 183 } 184 185 /** 186 * Returns the actual progress in percent.<p> 187 * 188 * The return value depends on the state of the progress/thread. This can be 189 * <ul> 190 * <li>the actual progress in percent with an optional description.</li> 191 * <li>the result as the html code for the list.</li> 192 * <li>an error message.</li> 193 * </ul><p> 194 * 195 * The result will be interpreted by the JavaScript method "updateProgressbar()".<p> 196 * 197 * @return the actual progress as a String 198 */ 199 public String getActualProgress() { 200 201 try { 202 CmsProgressThread thread; 203 if (getProgressThread(getKey()) != null) { 204 thread = m_threads.get(getKey()); 205 206 if (thread.isAlive()) { 207 // wait the configured time until to update the progress the first time 208 if (thread.getRuntime() < getShowWaitTime()) { 209 while ((thread.getRuntime() < getShowWaitTime()) && (thread.isAlive())) { 210 synchronized (this) { 211 wait(500); 212 } 213 } 214 } else { 215 // wait the configured refresh rate before returning 216 synchronized (this) { 217 wait(getRefreshRate()); 218 } 219 } 220 } 221 222 if (!thread.isAlive()) { 223 // is an error occurred in the execution of the thread? 224 if (thread.getError() != null) { 225 return createError( 226 Messages.get().getBundle(getJsp().getRequestContext().getLocale()).key( 227 Messages.GUI_PROGRESS_ERROR_IN_THREAD_0), 228 thread.getError()); 229 } 230 231 // return the result of the list created in the progress 232 return thread.getResult(); 233 } 234 235 // create and return the actual progress in percent with the description to be shown 236 StringBuffer result = new StringBuffer(); 237 238 result.append("PRO"); 239 result.append(thread.getProgress()); 240 result.append("%"); 241 result.append("|"); 242 result.append(thread.getDescription()); 243 244 return result.toString(); 245 } else { 246 if (LOG.isErrorEnabled()) { 247 LOG.error(Messages.get().getBundle().key(Messages.LOG_PROGRESS_THREAD_NOT_FOUND_1, getKey())); 248 } 249 return createError(Messages.get().getBundle(getJsp().getRequestContext().getLocale()).key( 250 Messages.GUI_PROGRESS_THREAD_NOT_FOUND_1, 251 getKey())); 252 } 253 254 } catch (Throwable t) { 255 if (LOG.isErrorEnabled()) { 256 LOG.error(Messages.get().getBundle().key(Messages.LOG_PROGRESS_ERROR_CALC_PROGRESS_0), t); 257 } 258 return createError( 259 Messages.get().getBundle(getJsp().getRequestContext().getLocale()).key( 260 Messages.GUI_PROGRESS_ERROR_CALCULATING_0), 261 t); 262 } 263 } 264 265 /** 266 * Returns the color of the progress bar.<p> 267 * 268 * @return the color of the progress bar 269 */ 270 public String getColor() { 271 272 return m_color; 273 } 274 275 /** 276 * Returns the name of the JavaScript method to call after progress is finished.<p> 277 * 278 * @return the name of the JavaScript method to call after progress is finished 279 */ 280 public String getJsFinishMethod() { 281 282 return m_jsFinishMethod; 283 } 284 285 /** 286 * Generates the necessary JavaScript inclusion code for this widget.<p> 287 * 288 * @return the JavaScript inclusion code 289 */ 290 public String getJsIncludes() { 291 292 StringBuffer result = new StringBuffer(); 293 294 result.append("<script src=\""); 295 result.append(CmsWorkplace.getSkinUri()); 296 result.append("commons/ajax.js\"></script>\n"); 297 298 result.append("<script >\n"); 299 300 // initialize global variables 301 result.append("\tvar progressState = 0;\n"); 302 result.append("\tvar progressResult = '';\n"); 303 304 // function update progress bar 305 result.append("\tfunction updateProgressBar(msg, state) {\n"); 306 307 // check state of progress 308 result.append("\t\tif (progressState != 1) {\n"); 309 result.append("\t\t\tprogressState = 0;\n"); 310 result.append("\t\t\treturn;\n"); 311 result.append("\t\t}\n"); 312 313 result.append("\t\tif (state == 'ok') {\n"); 314 315 // get all elements to use 316 result.append("\t\t\tvar bar = document.getElementById(\"progressbar_bar\");\n"); 317 result.append("\t\t\tvar percent = document.getElementById(\"progressbar_percent\");\n"); 318 result.append("\t\t\tvar wait = document.getElementById(\"progressbar_wait\");\n"); 319 result.append("\t\t\tvar desc = document.getElementById(\"progressbar_desc\");\n"); 320 321 result.append("\t\t\tif (msg != \"\") {\n"); 322 323 result.append("\t\t\t\tbar.parentNode.style.display = \"block\";\n"); 324 result.append("\t\t\t\tpercent.style.display = \"inline\";\n"); 325 result.append("\t\t\t\twait.style.display = \"none\";\n"); 326 result.append("\t\t\t\tdesc.style.display = \"block\";\n"); 327 328 // update progress 329 result.append("\t\t\t\tif (msg.substring(0,3) == \"PRO\") {\n"); 330 result.append("\t\t\t\t\tvar splitted = msg.split(\"|\");\n"); 331 result.append("\t\t\t\t\tbar.style.width = splitted[0].substr(3);\n"); 332 result.append("\t\t\t\t\tpercent.innerHTML = splitted[0].substr(3);\n"); 333 result.append("\t\t\t\t\tdesc.innerHTML = splitted[1];\n"); 334 result.append("\t\t\t\t\tmakeRequest('"); 335 result.append(getJsp().link("/system/workplace/commons/report-progress.jsp")); 336 result.append("', '"); 337 result.append(PARAMETER_KEY); 338 result.append("="); 339 result.append(getKey()); 340 result.append("&"); 341 result.append(PARAMETER_SHOWWAITTIME); 342 result.append("="); 343 result.append(getShowWaitTime()); 344 result.append("&"); 345 result.append(PARAMETER_REFRESHRATE); 346 result.append("="); 347 result.append(getRefreshRate()); 348 result.append("', 'updateProgressBar');\n"); 349 350 // set error message 351 result.append("\t\t\t\t} else if (msg.substring(0,3) == \"ERR\") {\n"); 352 result.append("\t\t\t\t\tsetProgressBarError(msg.substr(3));\n"); 353 354 // set result 355 result.append("\t\t\t\t} else {\n"); 356 result.append("\t\t\t\t\tprogressState = 0;\n"); 357 result.append("\t\t\t\t\tbar.style.width = \"100%\";\n"); 358 result.append("\t\t\t\t\tpercent.innerHTML = \"100%\";\n"); 359 result.append("\t\t\t\t\tprogressResult = msg;\n"); 360 361 result.append("\t\t\t\t\tbar.parentNode.style.display = \"none\";\n"); 362 result.append("\t\t\t\t\tpercent.style.display = \"none\";\n"); 363 result.append("\t\t\t\t\tdesc.style.display = \"none\";\n"); 364 result.append("\t\t\t\t\twait.style.display = \"block\";\n"); 365 366 result.append("\t\t\t\t\twindow.setTimeout(\""); 367 result.append(getJsFinishMethod()); 368 result.append("()\",100);\n"); 369 result.append("\t\t\t\t}\n"); 370 result.append("\t\t\t} else {\n"); 371 result.append("\t\t\t\tbar.style.width = \"100%\";\n"); 372 result.append("\t\t\t}\n"); 373 374 // fatal error returned by ajax 375 result.append("\t\t} else if (state == 'fatal') {\n"); 376 result.append("\t\t\tprogressState = 0;\n"); 377 result.append("\t\t\tsetProgressBarError(\""); 378 result.append( 379 org.opencms.workplace.Messages.get().getBundle(getJsp().getRequestContext().getLocale()).key( 380 org.opencms.workplace.Messages.GUI_AJAX_REPORT_GIVEUP_0)); 381 result.append("\");\n"); 382 383 // error returned by ajax 384 result.append("\t\t} else if (state == 'error') {\n"); 385 result.append("\t\t\tprogressState = 0;\n"); 386 result.append("\t\t\tsetProgressBarError(\""); 387 result.append( 388 org.opencms.workplace.Messages.get().getBundle(getJsp().getRequestContext().getLocale()).key( 389 org.opencms.workplace.Messages.GUI_AJAX_REPORT_ERROR_0)); 390 result.append(" \" + msg);\n"); 391 392 // wait returned by ajax -> display wait symbol 393 result.append("\t\t} else if (state == 'wait') {\n"); 394 result.append("\t\t\tbar.parentNode.style.display = \"none\";\n"); 395 result.append("\t\t\tpercent.style.display = \"none\";\n"); 396 result.append("\t\t\twait.style.display = \"block\";\n"); 397 result.append("\t\t}\n"); 398 result.append("\t}\n"); 399 400 // function set error 401 result.append("\tfunction setProgressBarError(msg) {\n"); 402 result.append("\t\tvar error = document.getElementById(\"progressbar_error\");\n"); 403 result.append("\t\tvar bar = document.getElementById(\"progressbar_bar\");\n"); 404 result.append("\t\tvar percent = document.getElementById(\"progressbar_percent\");\n"); 405 result.append("\t\tvar desc = document.getElementById(\"progressbar_desc\");\n"); 406 407 result.append("\t\terror.innerHTML = msg;\n"); 408 result.append("\t\terror.style.display = \"block\";\n"); 409 410 result.append("\t\tbar.style.display = \"none\";\n"); 411 result.append("\t\tpercent.style.display = \"none\";\n"); 412 result.append("\t\tdesc.style.display = \"none\";\n"); 413 414 result.append("\t}\n"); 415 416 // function reset progress bar 417 result.append("\tfunction resetProgressBar() {\n"); 418 result.append("\t\tvar bar = document.getElementById(\"progressbar_bar\");\n"); 419 result.append("\t\tbar.parentNode.style.display = \"inline\";\n"); 420 result.append("\t\tbar.style.width = \"0%\";\n"); 421 result.append("\t\tbar.style.display = \"block\";\n"); 422 423 result.append("\t\tvar percent = document.getElementById(\"progressbar_percent\");\n"); 424 result.append("\t\tpercent.innerHTML = \"0%\";\n"); 425 result.append("\t\tpercent.style.display = \"inline\";\n"); 426 427 result.append("\t\tvar error = document.getElementById(\"progressbar_error\");\n"); 428 result.append("\t\terror.innerHTML = \"\";\n"); 429 result.append("\t\terror.style.display = \"none\";\n"); 430 431 result.append("\t\tvar wait = document.getElementById(\"progressbar_wait\");\n"); 432 result.append("\t\twait.style.display = \"none\";\n"); 433 434 result.append("\t\tvar desc = document.getElementById(\"progressbar_desc\");\n"); 435 result.append("\t\tdesc.style.display = \"block\";\n"); 436 result.append("\t\tdesc.innerHTML = \"\";\n"); 437 438 result.append("\t\t\t\t\tprogressResult = \"\";\n"); 439 440 result.append("\t}\n"); 441 442 // function start progress bar 443 result.append("\tfunction startProgressBar() {\n"); 444 result.append("\t\tif (progressState > 0) {\n"); 445 result.append("\t\t\tprogressState = 2;\n"); 446 result.append("\t\t\twindow.setTimeout(\"startProgressBar()\","); 447 result.append(getRefreshRate()); 448 result.append(");\n"); 449 result.append("\t\t\treturn;\n"); 450 result.append("\t\t}\n"); 451 result.append("\t\tprogressState = 1;\n"); 452 result.append("\t\tmakeRequest('"); 453 result.append(getJsp().link("/system/workplace/commons/report-progress.jsp")); 454 result.append("', '"); 455 result.append(PARAMETER_KEY); 456 result.append("="); 457 result.append(getKey()); 458 result.append("&"); 459 result.append(PARAMETER_SHOWWAITTIME); 460 result.append("="); 461 result.append(getShowWaitTime()); 462 result.append("&"); 463 result.append(PARAMETER_REFRESHRATE); 464 result.append("="); 465 result.append(getRefreshRate()); 466 result.append("', 'updateProgressBar');\n"); 467 result.append("\t}\n"); 468 469 result.append("</script>\n"); 470 471 return result.toString(); 472 } 473 474 /** 475 * Returns the current JSP action element.<p> 476 * 477 * @return the the current JSP action element 478 */ 479 public CmsJspActionElement getJsp() { 480 481 return m_jsp; 482 } 483 484 /** 485 * Returns the unique key of the thread belonging to this widget.<p> 486 * 487 * @return the unique key of the thread belonging to this widget 488 */ 489 public String getKey() { 490 491 return m_key; 492 } 493 494 /** 495 * Returns the refresh rate in ms of the progress bar.<p> 496 * 497 * @return the refresh rate in ms of the progress bar 498 */ 499 public int getRefreshRate() { 500 501 return m_refreshRate; 502 } 503 504 /** 505 * Returns the time period the show the wait symbol before the progress bar is shown.<p> 506 * 507 * @return the time period the show the wait symbol before the progress bar is shown 508 */ 509 public int getShowWaitTime() { 510 511 return m_showWaitTime; 512 } 513 514 /** 515 * Generates the widget HTML for the progress bar.<p> 516 * 517 * @return the widget HTML for the progress bar 518 */ 519 public String getWidget() { 520 521 StringBuffer result = new StringBuffer(); 522 523 CmsProgressThread thread = getProgressThread(getKey()); 524 525 // if the thread is finished before the widget is rendered 526 // show directly the result 527 if ((thread != null) && (!thread.isAlive())) { 528 result.append("<script >\n"); 529 result.append("\tprogressState = 0;\n"); 530 result.append("\tprogressResult = '"); 531 result.append(CmsStringUtil.escapeJavaScript(getActualProgress())); 532 result.append("';\n"); 533 result.append("\t"); 534 result.append(getJsFinishMethod()); 535 result.append("();\n"); 536 result.append("</script>\n"); 537 } else { 538 // check if to show the wait symbol 539 boolean showWait = false; 540 if (getShowWaitTime() > 0) { 541 // show if the thread is running and the time running is smaller than the configured 542 if ((thread != null) && (thread.isAlive()) && (thread.getRuntime() < getShowWaitTime())) { 543 showWait = true; 544 } else if ((thread == null) && (getShowWaitTime() > 0)) { 545 // show if there is no thread 546 showWait = true; 547 } 548 } 549 550 result.append("<div id=\"progressbar_desc\" style=\"margin-bottom:5px;display:"); 551 result.append(showWait ? "none" : "block"); 552 result.append("\"></div>"); 553 554 result.append("<div style=\"width:"); 555 result.append(getWidth()); 556 result.append(";border-width:1px;border-style:solid;padding:0px;margin:0px;float:left;display:"); 557 result.append(showWait ? "none" : "inline"); 558 result.append(";\">\n"); 559 result.append("\t<div id=\"progressbar_bar\" style=\"width:0%;background-color:"); 560 result.append(getColor()); 561 result.append(";\"> </div>\n"); 562 result.append("</div>\n"); 563 result.append(" "); 564 result.append("<div id=\"progressbar_percent\" style=\"display:"); 565 result.append(showWait ? "none" : "inline"); 566 result.append(";\" >0%</div>\n"); 567 568 result.append( 569 "<div id=\"progressbar_error\" style=\"display:none;color:#B40000;font-weight:bold;\"></div>\n"); 570 571 result.append("<div id=\"progressbar_wait\" style=\"display:"); 572 result.append(showWait ? "block" : "none"); 573 result.append(";color:#000099;font-weight:bold;\"><img src=\""); 574 result.append(CmsWorkplace.getSkinUri()); 575 result.append("commons/wait.gif\" width='32' height='32' alt='' align='absmiddle' />"); 576 result.append( 577 org.opencms.workplace.Messages.get().getBundle(getJsp().getRequestContext().getLocale()).key( 578 org.opencms.workplace.Messages.GUI_AJAX_REPORT_WAIT_0)); 579 result.append("</div>\n"); 580 581 result.append("<script >\n"); 582 result.append("\tstartProgressBar();\n"); 583 result.append("</script>\n"); 584 } 585 586 return result.toString(); 587 } 588 589 /** 590 * Returns the width of the progress bar.<p> 591 * 592 * @return the width of the progress bar 593 */ 594 public String getWidth() { 595 596 return m_width; 597 } 598 599 /** 600 * Sets the color of the progress bar.<p> 601 * 602 * @param color the color of the progress bar to set 603 */ 604 public void setColor(String color) { 605 606 m_color = color; 607 } 608 609 /** 610 * Sets the name of the JavaScript method to call after progress is finished.<p> 611 * 612 * @param jsFinishMethod the name of the JavaScript method to call after progress is finished to set 613 */ 614 public void setJsFinishMethod(String jsFinishMethod) { 615 616 m_jsFinishMethod = jsFinishMethod; 617 } 618 619 /** 620 * Sets the refresh rate in ms of the progress bar.<p> 621 * 622 * @param refreshRate the refresh rate in ms of the progress bar to set 623 */ 624 public void setRefreshRate(int refreshRate) { 625 626 m_refreshRate = refreshRate; 627 } 628 629 /** 630 * Sets the time period the show the wait symbol before the progress bar is shown.<p> 631 * 632 * @param showWaitTime the time period the show the wait symbol before the progress bar is shown to set 633 */ 634 public void setShowWaitTime(int showWaitTime) { 635 636 m_showWaitTime = showWaitTime; 637 } 638 639 /** 640 * Sets the width of the progress bar.<p> 641 * 642 * @param width the width of the progress bar to set 643 */ 644 public void setWidth(String width) { 645 646 m_width = width; 647 } 648 649 /** 650 * Starts a thread for the progress on the given list.<p> 651 * 652 * @param list the list to use for the progress bar 653 */ 654 public void startProgress(A_CmsListDialog list) { 655 656 startProgress(list, false); 657 } 658 659 /** 660 * Starts a thread for the progress on the given list.<p> 661 * 662 * @param list the list to use for the progress bar 663 * @param abortExisting if true then an already existing thread will be killed 664 */ 665 public void startProgress(A_CmsListDialog list, boolean abortExisting) { 666 667 // check the list 668 if (list == null) { 669 throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_PROGRESS_START_INVALID_LIST_0)); 670 } 671 672 // check if created key already exists 673 if (m_threads.get(getKey()) != null) { 674 if (abortExisting) { 675 if (LOG.isDebugEnabled()) { 676 LOG.debug(Messages.get().getBundle().key(Messages.LOG_PROGRESS_INTERRUPT_THREAD_1, getKey())); 677 } 678 Thread thread = m_threads.get(getKey()); 679 thread.interrupt(); 680 } else { 681 throw new CmsIllegalStateException( 682 Messages.get().container(Messages.ERR_PROGRESS_START_THREAD_EXISTS_0)); 683 } 684 } 685 686 // create the thread 687 CmsProgressThread thread = new CmsProgressThread(list, getKey(), list.getLocale()); 688 689 Map<String, CmsProgressThread> threadsAbandoned = new HashMap<String, CmsProgressThread>(); 690 Map<String, CmsProgressThread> threadsAlive = new HashMap<String, CmsProgressThread>(); 691 synchronized (m_threads) { 692 693 // clean up abandoned threads 694 for (Iterator<Map.Entry<String, CmsProgressThread>> iter = m_threads.entrySet().iterator(); iter.hasNext();) { 695 Map.Entry<String, CmsProgressThread> entry = iter.next(); 696 CmsProgressThread value = entry.getValue(); 697 698 if ((!value.isAlive()) && ((System.currentTimeMillis() - value.getFinishTime()) > CLEANUP_PERIOD)) { 699 threadsAbandoned.put(entry.getKey(), value); 700 } else { 701 threadsAlive.put(entry.getKey(), value); 702 } 703 } 704 705 // add and start new thread 706 threadsAlive.put(thread.getKey(), thread); 707 thread.start(); 708 709 m_threads = threadsAlive; 710 } 711 712 if (LOG.isDebugEnabled()) { 713 for (Iterator<String> iter = threadsAbandoned.keySet().iterator(); iter.hasNext();) { 714 LOG.debug(Messages.get().getBundle().key(Messages.LOG_PROGRESS_CLEAN_UP_THREAD_1, iter.next())); 715 } 716 } 717 } 718 719 /** 720 * Creates the html code for the given error message.<p> 721 * 722 * @param errorMsg the error message to place in the html code 723 * 724 * @return the html code for the error message 725 */ 726 private String createError(String errorMsg) { 727 728 StringBuffer result = new StringBuffer(); 729 730 result.append("ERR"); 731 result.append(errorMsg); 732 733 return result.toString(); 734 } 735 736 /** 737 * Creates the html code for the given error message and the 738 * provided Exception.<p> 739 * 740 * @param errorMsg the error message to place in the html code 741 * @param t the exception to add to the error message 742 * 743 * @return the html code for the error message 744 */ 745 private String createError(String errorMsg, Throwable t) { 746 747 StringBuffer msg = new StringBuffer(); 748 msg.append(errorMsg); 749 msg.append("\n"); 750 msg.append(t.getMessage()); 751 msg.append("\n"); 752 msg.append(CmsException.getStackTraceAsString(t)); 753 754 return createError(msg.toString()); 755 } 756 757}