001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://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: https://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: https://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 org.apache.commons.logging.Log; 045 046import jakarta.servlet.http.HttpServletRequest; 047import jakarta.servlet.http.HttpServletResponse; 048import jakarta.servlet.jsp.PageContext; 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( 250 Messages.get().getBundle(getJsp().getRequestContext().getLocale()).key( 251 Messages.GUI_PROGRESS_THREAD_NOT_FOUND_1, 252 getKey())); 253 } 254 255 } catch (Throwable t) { 256 if (LOG.isErrorEnabled()) { 257 LOG.error(Messages.get().getBundle().key(Messages.LOG_PROGRESS_ERROR_CALC_PROGRESS_0), t); 258 } 259 return createError( 260 Messages.get().getBundle(getJsp().getRequestContext().getLocale()).key( 261 Messages.GUI_PROGRESS_ERROR_CALCULATING_0), 262 t); 263 } 264 } 265 266 /** 267 * Returns the color of the progress bar.<p> 268 * 269 * @return the color of the progress bar 270 */ 271 public String getColor() { 272 273 return m_color; 274 } 275 276 /** 277 * Returns the name of the JavaScript method to call after progress is finished.<p> 278 * 279 * @return the name of the JavaScript method to call after progress is finished 280 */ 281 public String getJsFinishMethod() { 282 283 return m_jsFinishMethod; 284 } 285 286 /** 287 * Generates the necessary JavaScript inclusion code for this widget.<p> 288 * 289 * @return the JavaScript inclusion code 290 */ 291 public String getJsIncludes() { 292 293 StringBuffer result = new StringBuffer(); 294 295 result.append("<script src=\""); 296 result.append(CmsWorkplace.getSkinUri()); 297 result.append("commons/ajax.js\"></script>\n"); 298 299 result.append("<script >\n"); 300 301 // initialize global variables 302 result.append("\tvar progressState = 0;\n"); 303 result.append("\tvar progressResult = '';\n"); 304 305 // function update progress bar 306 result.append("\tfunction updateProgressBar(msg, state) {\n"); 307 308 // check state of progress 309 result.append("\t\tif (progressState != 1) {\n"); 310 result.append("\t\t\tprogressState = 0;\n"); 311 result.append("\t\t\treturn;\n"); 312 result.append("\t\t}\n"); 313 314 result.append("\t\tif (state == 'ok') {\n"); 315 316 // get all elements to use 317 result.append("\t\t\tvar bar = document.getElementById(\"progressbar_bar\");\n"); 318 result.append("\t\t\tvar percent = document.getElementById(\"progressbar_percent\");\n"); 319 result.append("\t\t\tvar wait = document.getElementById(\"progressbar_wait\");\n"); 320 result.append("\t\t\tvar desc = document.getElementById(\"progressbar_desc\");\n"); 321 322 result.append("\t\t\tif (msg != \"\") {\n"); 323 324 result.append("\t\t\t\tbar.parentNode.style.display = \"block\";\n"); 325 result.append("\t\t\t\tpercent.style.display = \"inline\";\n"); 326 result.append("\t\t\t\twait.style.display = \"none\";\n"); 327 result.append("\t\t\t\tdesc.style.display = \"block\";\n"); 328 329 // update progress 330 result.append("\t\t\t\tif (msg.substring(0,3) == \"PRO\") {\n"); 331 result.append("\t\t\t\t\tvar splitted = msg.split(\"|\");\n"); 332 result.append("\t\t\t\t\tbar.style.width = splitted[0].substr(3);\n"); 333 result.append("\t\t\t\t\tpercent.innerHTML = splitted[0].substr(3);\n"); 334 result.append("\t\t\t\t\tdesc.innerHTML = splitted[1];\n"); 335 result.append("\t\t\t\t\tmakeRequest('"); 336 result.append(getJsp().link("/system/workplace/commons/report-progress.jsp")); 337 result.append("', '"); 338 result.append(PARAMETER_KEY); 339 result.append("="); 340 result.append(getKey()); 341 result.append("&"); 342 result.append(PARAMETER_SHOWWAITTIME); 343 result.append("="); 344 result.append(getShowWaitTime()); 345 result.append("&"); 346 result.append(PARAMETER_REFRESHRATE); 347 result.append("="); 348 result.append(getRefreshRate()); 349 result.append("', 'updateProgressBar');\n"); 350 351 // set error message 352 result.append("\t\t\t\t} else if (msg.substring(0,3) == \"ERR\") {\n"); 353 result.append("\t\t\t\t\tsetProgressBarError(msg.substr(3));\n"); 354 355 // set result 356 result.append("\t\t\t\t} else {\n"); 357 result.append("\t\t\t\t\tprogressState = 0;\n"); 358 result.append("\t\t\t\t\tbar.style.width = \"100%\";\n"); 359 result.append("\t\t\t\t\tpercent.innerHTML = \"100%\";\n"); 360 result.append("\t\t\t\t\tprogressResult = msg;\n"); 361 362 result.append("\t\t\t\t\tbar.parentNode.style.display = \"none\";\n"); 363 result.append("\t\t\t\t\tpercent.style.display = \"none\";\n"); 364 result.append("\t\t\t\t\tdesc.style.display = \"none\";\n"); 365 result.append("\t\t\t\t\twait.style.display = \"block\";\n"); 366 367 result.append("\t\t\t\t\twindow.setTimeout(\""); 368 result.append(getJsFinishMethod()); 369 result.append("()\",100);\n"); 370 result.append("\t\t\t\t}\n"); 371 result.append("\t\t\t} else {\n"); 372 result.append("\t\t\t\tbar.style.width = \"100%\";\n"); 373 result.append("\t\t\t}\n"); 374 375 // fatal error returned by ajax 376 result.append("\t\t} else if (state == 'fatal') {\n"); 377 result.append("\t\t\tprogressState = 0;\n"); 378 result.append("\t\t\tsetProgressBarError(\""); 379 result.append( 380 org.opencms.workplace.Messages.get().getBundle(getJsp().getRequestContext().getLocale()).key( 381 org.opencms.workplace.Messages.GUI_AJAX_REPORT_GIVEUP_0)); 382 result.append("\");\n"); 383 384 // error returned by ajax 385 result.append("\t\t} else if (state == 'error') {\n"); 386 result.append("\t\t\tprogressState = 0;\n"); 387 result.append("\t\t\tsetProgressBarError(\""); 388 result.append( 389 org.opencms.workplace.Messages.get().getBundle(getJsp().getRequestContext().getLocale()).key( 390 org.opencms.workplace.Messages.GUI_AJAX_REPORT_ERROR_0)); 391 result.append(" \" + msg);\n"); 392 393 // wait returned by ajax -> display wait symbol 394 result.append("\t\t} else if (state == 'wait') {\n"); 395 result.append("\t\t\tbar.parentNode.style.display = \"none\";\n"); 396 result.append("\t\t\tpercent.style.display = \"none\";\n"); 397 result.append("\t\t\twait.style.display = \"block\";\n"); 398 result.append("\t\t}\n"); 399 result.append("\t}\n"); 400 401 // function set error 402 result.append("\tfunction setProgressBarError(msg) {\n"); 403 result.append("\t\tvar error = document.getElementById(\"progressbar_error\");\n"); 404 result.append("\t\tvar bar = document.getElementById(\"progressbar_bar\");\n"); 405 result.append("\t\tvar percent = document.getElementById(\"progressbar_percent\");\n"); 406 result.append("\t\tvar desc = document.getElementById(\"progressbar_desc\");\n"); 407 408 result.append("\t\terror.innerHTML = msg;\n"); 409 result.append("\t\terror.style.display = \"block\";\n"); 410 411 result.append("\t\tbar.style.display = \"none\";\n"); 412 result.append("\t\tpercent.style.display = \"none\";\n"); 413 result.append("\t\tdesc.style.display = \"none\";\n"); 414 415 result.append("\t}\n"); 416 417 // function reset progress bar 418 result.append("\tfunction resetProgressBar() {\n"); 419 result.append("\t\tvar bar = document.getElementById(\"progressbar_bar\");\n"); 420 result.append("\t\tbar.parentNode.style.display = \"inline\";\n"); 421 result.append("\t\tbar.style.width = \"0%\";\n"); 422 result.append("\t\tbar.style.display = \"block\";\n"); 423 424 result.append("\t\tvar percent = document.getElementById(\"progressbar_percent\");\n"); 425 result.append("\t\tpercent.innerHTML = \"0%\";\n"); 426 result.append("\t\tpercent.style.display = \"inline\";\n"); 427 428 result.append("\t\tvar error = document.getElementById(\"progressbar_error\");\n"); 429 result.append("\t\terror.innerHTML = \"\";\n"); 430 result.append("\t\terror.style.display = \"none\";\n"); 431 432 result.append("\t\tvar wait = document.getElementById(\"progressbar_wait\");\n"); 433 result.append("\t\twait.style.display = \"none\";\n"); 434 435 result.append("\t\tvar desc = document.getElementById(\"progressbar_desc\");\n"); 436 result.append("\t\tdesc.style.display = \"block\";\n"); 437 result.append("\t\tdesc.innerHTML = \"\";\n"); 438 439 result.append("\t\t\t\t\tprogressResult = \"\";\n"); 440 441 result.append("\t}\n"); 442 443 // function start progress bar 444 result.append("\tfunction startProgressBar() {\n"); 445 result.append("\t\tif (progressState > 0) {\n"); 446 result.append("\t\t\tprogressState = 2;\n"); 447 result.append("\t\t\twindow.setTimeout(\"startProgressBar()\","); 448 result.append(getRefreshRate()); 449 result.append(");\n"); 450 result.append("\t\t\treturn;\n"); 451 result.append("\t\t}\n"); 452 result.append("\t\tprogressState = 1;\n"); 453 result.append("\t\tmakeRequest('"); 454 result.append(getJsp().link("/system/workplace/commons/report-progress.jsp")); 455 result.append("', '"); 456 result.append(PARAMETER_KEY); 457 result.append("="); 458 result.append(getKey()); 459 result.append("&"); 460 result.append(PARAMETER_SHOWWAITTIME); 461 result.append("="); 462 result.append(getShowWaitTime()); 463 result.append("&"); 464 result.append(PARAMETER_REFRESHRATE); 465 result.append("="); 466 result.append(getRefreshRate()); 467 result.append("', 'updateProgressBar');\n"); 468 result.append("\t}\n"); 469 470 result.append("</script>\n"); 471 472 return result.toString(); 473 } 474 475 /** 476 * Returns the current JSP action element.<p> 477 * 478 * @return the the current JSP action element 479 */ 480 public CmsJspActionElement getJsp() { 481 482 return m_jsp; 483 } 484 485 /** 486 * Returns the unique key of the thread belonging to this widget.<p> 487 * 488 * @return the unique key of the thread belonging to this widget 489 */ 490 public String getKey() { 491 492 return m_key; 493 } 494 495 /** 496 * Returns the refresh rate in ms of the progress bar.<p> 497 * 498 * @return the refresh rate in ms of the progress bar 499 */ 500 public int getRefreshRate() { 501 502 return m_refreshRate; 503 } 504 505 /** 506 * Returns the time period the show the wait symbol before the progress bar is shown.<p> 507 * 508 * @return the time period the show the wait symbol before the progress bar is shown 509 */ 510 public int getShowWaitTime() { 511 512 return m_showWaitTime; 513 } 514 515 /** 516 * Generates the widget HTML for the progress bar.<p> 517 * 518 * @return the widget HTML for the progress bar 519 */ 520 public String getWidget() { 521 522 StringBuffer result = new StringBuffer(); 523 524 CmsProgressThread thread = getProgressThread(getKey()); 525 526 // if the thread is finished before the widget is rendered 527 // show directly the result 528 if ((thread != null) && (!thread.isAlive())) { 529 result.append("<script >\n"); 530 result.append("\tprogressState = 0;\n"); 531 result.append("\tprogressResult = '"); 532 result.append(CmsStringUtil.escapeJavaScript(getActualProgress())); 533 result.append("';\n"); 534 result.append("\t"); 535 result.append(getJsFinishMethod()); 536 result.append("();\n"); 537 result.append("</script>\n"); 538 } else { 539 // check if to show the wait symbol 540 boolean showWait = false; 541 if (getShowWaitTime() > 0) { 542 // show if the thread is running and the time running is smaller than the configured 543 if ((thread != null) && (thread.isAlive()) && (thread.getRuntime() < getShowWaitTime())) { 544 showWait = true; 545 } else if ((thread == null) && (getShowWaitTime() > 0)) { 546 // show if there is no thread 547 showWait = true; 548 } 549 } 550 551 result.append("<div id=\"progressbar_desc\" style=\"margin-bottom:5px;display:"); 552 result.append(showWait ? "none" : "block"); 553 result.append("\"></div>"); 554 555 result.append("<div style=\"width:"); 556 result.append(getWidth()); 557 result.append(";border-width:1px;border-style:solid;padding:0px;margin:0px;float:left;display:"); 558 result.append(showWait ? "none" : "inline"); 559 result.append(";\">\n"); 560 result.append("\t<div id=\"progressbar_bar\" style=\"width:0%;background-color:"); 561 result.append(getColor()); 562 result.append(";\"> </div>\n"); 563 result.append("</div>\n"); 564 result.append(" "); 565 result.append("<div id=\"progressbar_percent\" style=\"display:"); 566 result.append(showWait ? "none" : "inline"); 567 result.append(";\" >0%</div>\n"); 568 569 result.append( 570 "<div id=\"progressbar_error\" style=\"display:none;color:#B40000;font-weight:bold;\"></div>\n"); 571 572 result.append("<div id=\"progressbar_wait\" style=\"display:"); 573 result.append(showWait ? "block" : "none"); 574 result.append(";color:#000099;font-weight:bold;\"><img src=\""); 575 result.append(CmsWorkplace.getSkinUri()); 576 result.append("commons/wait.gif\" width='32' height='32' alt='' align='absmiddle' />"); 577 result.append( 578 org.opencms.workplace.Messages.get().getBundle(getJsp().getRequestContext().getLocale()).key( 579 org.opencms.workplace.Messages.GUI_AJAX_REPORT_WAIT_0)); 580 result.append("</div>\n"); 581 582 result.append("<script >\n"); 583 result.append("\tstartProgressBar();\n"); 584 result.append("</script>\n"); 585 } 586 587 return result.toString(); 588 } 589 590 /** 591 * Returns the width of the progress bar.<p> 592 * 593 * @return the width of the progress bar 594 */ 595 public String getWidth() { 596 597 return m_width; 598 } 599 600 /** 601 * Sets the color of the progress bar.<p> 602 * 603 * @param color the color of the progress bar to set 604 */ 605 public void setColor(String color) { 606 607 m_color = color; 608 } 609 610 /** 611 * Sets the name of the JavaScript method to call after progress is finished.<p> 612 * 613 * @param jsFinishMethod the name of the JavaScript method to call after progress is finished to set 614 */ 615 public void setJsFinishMethod(String jsFinishMethod) { 616 617 m_jsFinishMethod = jsFinishMethod; 618 } 619 620 /** 621 * Sets the refresh rate in ms of the progress bar.<p> 622 * 623 * @param refreshRate the refresh rate in ms of the progress bar to set 624 */ 625 public void setRefreshRate(int refreshRate) { 626 627 m_refreshRate = refreshRate; 628 } 629 630 /** 631 * Sets the time period the show the wait symbol before the progress bar is shown.<p> 632 * 633 * @param showWaitTime the time period the show the wait symbol before the progress bar is shown to set 634 */ 635 public void setShowWaitTime(int showWaitTime) { 636 637 m_showWaitTime = showWaitTime; 638 } 639 640 /** 641 * Sets the width of the progress bar.<p> 642 * 643 * @param width the width of the progress bar to set 644 */ 645 public void setWidth(String width) { 646 647 m_width = width; 648 } 649 650 /** 651 * Starts a thread for the progress on the given list.<p> 652 * 653 * @param list the list to use for the progress bar 654 */ 655 public void startProgress(A_CmsListDialog list) { 656 657 startProgress(list, false); 658 } 659 660 /** 661 * Starts a thread for the progress on the given list.<p> 662 * 663 * @param list the list to use for the progress bar 664 * @param abortExisting if true then an already existing thread will be killed 665 */ 666 public void startProgress(A_CmsListDialog list, boolean abortExisting) { 667 668 // check the list 669 if (list == null) { 670 throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_PROGRESS_START_INVALID_LIST_0)); 671 } 672 673 // check if created key already exists 674 if (m_threads.get(getKey()) != null) { 675 if (abortExisting) { 676 if (LOG.isDebugEnabled()) { 677 LOG.debug(Messages.get().getBundle().key(Messages.LOG_PROGRESS_INTERRUPT_THREAD_1, getKey())); 678 } 679 Thread thread = m_threads.get(getKey()); 680 thread.interrupt(); 681 } else { 682 throw new CmsIllegalStateException( 683 Messages.get().container(Messages.ERR_PROGRESS_START_THREAD_EXISTS_0)); 684 } 685 } 686 687 // create the thread 688 CmsProgressThread thread = new CmsProgressThread(list, getKey(), list.getLocale()); 689 690 Map<String, CmsProgressThread> threadsAbandoned = new HashMap<String, CmsProgressThread>(); 691 Map<String, CmsProgressThread> threadsAlive = new HashMap<String, CmsProgressThread>(); 692 synchronized (m_threads) { 693 694 // clean up abandoned threads 695 for (Iterator<Map.Entry<String, CmsProgressThread>> iter = m_threads.entrySet().iterator(); iter.hasNext();) { 696 Map.Entry<String, CmsProgressThread> entry = iter.next(); 697 CmsProgressThread value = entry.getValue(); 698 699 if ((!value.isAlive()) && ((System.currentTimeMillis() - value.getFinishTime()) > CLEANUP_PERIOD)) { 700 threadsAbandoned.put(entry.getKey(), value); 701 } else { 702 threadsAlive.put(entry.getKey(), value); 703 } 704 } 705 706 // add and start new thread 707 threadsAlive.put(thread.getKey(), thread); 708 thread.start(); 709 710 m_threads = threadsAlive; 711 } 712 713 if (LOG.isDebugEnabled()) { 714 for (Iterator<String> iter = threadsAbandoned.keySet().iterator(); iter.hasNext();) { 715 LOG.debug(Messages.get().getBundle().key(Messages.LOG_PROGRESS_CLEAN_UP_THREAD_1, iter.next())); 716 } 717 } 718 } 719 720 /** 721 * Creates the html code for the given error message.<p> 722 * 723 * @param errorMsg the error message to place in the html code 724 * 725 * @return the html code for the error message 726 */ 727 private String createError(String errorMsg) { 728 729 StringBuffer result = new StringBuffer(); 730 731 result.append("ERR"); 732 result.append(errorMsg); 733 734 return result.toString(); 735 } 736 737 /** 738 * Creates the html code for the given error message and the 739 * provided Exception.<p> 740 * 741 * @param errorMsg the error message to place in the html code 742 * @param t the exception to add to the error message 743 * 744 * @return the html code for the error message 745 */ 746 private String createError(String errorMsg, Throwable t) { 747 748 StringBuffer msg = new StringBuffer(); 749 msg.append(errorMsg); 750 msg.append("\n"); 751 msg.append(t.getMessage()); 752 msg.append("\n"); 753 msg.append(CmsException.getStackTraceAsString(t)); 754 755 return createError(msg.toString()); 756 } 757 758}