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.util; 029 030import org.opencms.i18n.CmsEncoder; 031import org.opencms.main.CmsIllegalArgumentException; 032import org.opencms.main.CmsLog; 033import org.opencms.main.CmsRuntimeException; 034import org.opencms.main.OpenCms; 035 036import java.io.BufferedReader; 037import java.io.ByteArrayOutputStream; 038import java.io.File; 039import java.io.FileInputStream; 040import java.io.FileNotFoundException; 041import java.io.IOException; 042import java.io.InputStreamReader; 043import java.io.LineNumberReader; 044import java.io.OutputStreamWriter; 045import java.nio.charset.Charset; 046import java.nio.charset.IllegalCharsetNameException; 047import java.nio.charset.UnsupportedCharsetException; 048import java.util.ArrayList; 049import java.util.List; 050 051import org.apache.commons.logging.Log; 052 053/** 054 * The representation of a RFS file along with the settings to provide 055 * access to certain portions (amount of lines) of it. <p> 056 * 057 * Most often the underlying file will be the OpenCms logfile. <p> 058 * 059 * The portion of the file that is shown is defined by a "window" of "windowSize" lines of text 060 * at a position "windowPosition" which is an enumeration of windows in ascending order. <p> 061 * 062 * @since 6.0.0 063 */ 064public class CmsRfsFileViewer implements Cloneable { 065 066 /** The log object for this class. */ 067 protected static final Log LOG = CmsLog.getLog(CmsRfsFileViewer.class); 068 069 /** The path to the underlying file. */ 070 protected String m_filePath; 071 072 /** The path to the root for all accessible files. */ 073 protected String m_rootPath; 074 075 /** The current window (numbered from zero to amount of possible different windows). */ 076 protected int m_windowPos; 077 078 /** The amount of lines to show. */ 079 protected int m_windowSize; 080 081 /** The additional allowed RFS roots for viewing files. */ 082 private List<String> m_additionalRoots; 083 084 /** Decides whether the view onto the underlying file via readFilePortion is enabled. */ 085 private boolean m_enabled; 086 087 /** The character encoding of the underlying file. */ 088 private Charset m_fileEncoding; 089 090 /** 091 * If value is <code>true</code>, all setter methods will throw a 092 * <code>{@link CmsRuntimeException}</code><p>. 093 * 094 * Only the method <code>{@link #clone()}</code> returns a clone that has set this 095 * member to <code>false</code> allowing modification to take place.<p> 096 */ 097 private boolean m_frozen; 098 099 /** 100 * If true the represented file is a standard OpenCms log file and may be displayed 101 * in more convenient ways (in future versions) because the format is known. 102 */ 103 private boolean m_isLogfile; 104 105 /** 106 * Creates an instance with default settings that tries to use the log file path obtained 107 * from <code>{@link OpenCms}'s {@link org.opencms.main.CmsSystemInfo}</code> instance.<p> 108 * 109 * If the log file path is invalid or not configured correctly a logging is performed and the 110 * path remains empty to allow user-specified file selection.<p> 111 */ 112 public CmsRfsFileViewer() { 113 114 m_rootPath = getLogFolderPath(); 115 m_isLogfile = true; 116 // system default charset: see http://java.sun.com/j2se/corejava/intl/reference/faqs/index.html#default-encoding 117 m_fileEncoding = Charset.forName(new OutputStreamWriter(new ByteArrayOutputStream()).getEncoding()); 118 m_enabled = true; 119 m_windowSize = 1000; 120 121 } 122 123 /** 124 * Returns a clone of this file view settings that is not "frozen" and therefore allows modifications.<p> 125 * 126 * Every instance that plans to modify settings has to obtain a clone first that may be 127 * modified. The original instance returned from 128 * (<code>{@link org.opencms.workplace.CmsWorkplaceManager#getFileViewSettings()}</code>) will throw 129 * a <code>{@link CmsRuntimeException}</code> for each setter invocation. <p> 130 * 131 * @return a clone of this file view settings that is not "frozen" and therefore allows modifications 132 */ 133 @Override 134 public Object clone() { 135 136 // first run after installation: filePath & rootPath is null: 137 if (m_filePath == null) { 138 // below that runlevel the following call will fail (not initialized from config yet): 139 if (OpenCms.getRunLevel() >= OpenCms.RUNLEVEL_3_SHELL_ACCESS) { 140 m_filePath = OpenCms.getSystemInfo().getLogFileRfsPath(); 141 } 142 } 143 if (m_rootPath == null) { 144 m_rootPath = getLogFolderPath(); 145 } 146 CmsRfsFileViewer clone = new CmsRfsFileViewer(); 147 clone.m_rootPath = m_rootPath; 148 try { 149 // strings are immutable: no outside modification possible. 150 clone.setFilePath(m_filePath); 151 } catch (CmsRfsException e) { 152 // will never happen because m_filePath was verified in setFilePath of this instance. 153 } catch (CmsRuntimeException e) { 154 // will never happen because m_filePath was verified in setFilePath of this instance. 155 } 156 clone.m_fileEncoding = m_fileEncoding; 157 clone.m_isLogfile = m_isLogfile; 158 clone.m_enabled = m_enabled; 159 //clone.m_windowPos = m_windowPos; 160 clone.setWindowSize(m_windowSize); 161 // allow clone-modifications. 162 clone.m_frozen = false; 163 return clone; 164 } 165 166 /** 167 * Returns the canonical name of the character encoding of the underlying file.<p> 168 * 169 * If no special choice is fed into 170 * <code>{@link #setFileEncoding(String)}</code> before this call 171 * always the system default character encoding is returned.<p> 172 * 173 * This value may be ignored outside and will be ignored inside if the 174 * underlying does not contain textual content.<p> 175 * 176 * @return the canonical name of the character encoding of the underlying file 177 */ 178 public String getFileEncoding() { 179 180 return m_fileEncoding.name(); 181 } 182 183 /** 184 * Returns the path denoting the file that is accessed.<p> 185 * 186 * @return the path denoting the file that is accessed 187 */ 188 public String getFilePath() { 189 190 return m_filePath; 191 } 192 193 /** 194 * Returns true if the view's internal file path points to a log file in standard OpenCms format.<p> 195 * 196 * @return true if the view's internal file path points to a log file in standard OpenCms format 197 */ 198 public boolean getIsLogfile() { 199 200 // method name is bean-convention of apache.commons.beanutils (unlike eclipse's convention for booleans) 201 return m_isLogfile; 202 } 203 204 /** 205 * Returns the path denoting the root folder for all accessible files.<p> 206 * 207 * @return the path denoting the root folder for all accessible files 208 */ 209 public String getRootPath() { 210 211 return m_rootPath; 212 } 213 214 /** 215 * Returns the start position of the current display.<p> 216 * 217 * This is a count of "windows" that 218 * consist of viewable text with "windowSize" lines of text (for a non-standard log file) or 219 * log-entries (for a standard log file).<p> 220 * 221 * @return the start position of the current display 222 */ 223 public int getWindowPos() { 224 225 return m_windowPos; 226 } 227 228 /** 229 * Get the amount of lines (or entries depending on whether a standard log file is shown) 230 * to display per page. <p> 231 * 232 * @return the amount of lines to display per page 233 */ 234 public int getWindowSize() { 235 236 return m_windowSize; 237 } 238 239 /** 240 * Returns true if this view upon the underlying file via 241 * <code>{@link #readFilePortion()}</code> is enabled.<p> 242 * 243 * 244 * @return true if this view upon the underlying file via 245 * <code>{@link #readFilePortion()}</code> is enabled.<p> 246 */ 247 public boolean isEnabled() { 248 249 return m_enabled; 250 } 251 252 /** 253 * Return the view portion of lines of text from the underlying file or an 254 * empty String if <code>{@link #isEnabled()}</code> returns <code>false</code>.<p> 255 * 256 * @return the view portion of lines of text from the underlying file or an 257 * empty String if <code>{@link #isEnabled()}</code> returns <code>false</code> 258 * @throws CmsRfsException if something goes wrong 259 */ 260 public String readFilePortion() throws CmsRfsException { 261 262 if (m_enabled) { 263 // if we want to view the log file we have to set the internal m_windowPos to the last window 264 // to view the end: 265 int lines = -1; 266 int startLine; 267 if (m_isLogfile) { 268 lines = scrollToFileEnd(); 269 // for logfile mode we show the last window of window size: 270 // it could be possible that only 4 lines are in the last window 271 // (e.g.: 123 lines with windowsize 10 -> last window has 3 lines) 272 // so we ignore the window semantics and show the n last lines: 273 startLine = lines - m_windowSize; 274 } else { 275 m_windowPos = 0; 276 startLine = m_windowPos * m_windowSize; 277 } 278 LineNumberReader reader = null; 279 try { 280 // don't make the buffer too big, just big enough for windowSize lines (estimation: avg. of 200 characters per line) 281 // to save reading too much (this optimizes to read the first windows, much later windows will be slower...) 282 reader = new LineNumberReader( 283 new BufferedReader(new InputStreamReader(new FileInputStream(m_filePath), m_fileEncoding)), 284 m_windowSize * 200); 285 int currentLine = 0; 286 // skip the lines to the current window: 287 while (startLine > currentLine) { 288 reader.readLine(); 289 currentLine++; 290 } 291 StringBuffer result = new StringBuffer(); 292 String read = reader.readLine(); 293 294 for (int i = m_windowSize; (i > 0) && (read != null); i--) { 295 result.append(read); 296 result.append('\n'); 297 read = reader.readLine(); 298 } 299 300 return CmsEncoder.escapeXml(result.toString()); 301 } catch (IOException ioex) { 302 CmsRfsException ex = new CmsRfsException( 303 Messages.get().container(Messages.ERR_FILE_ARG_ACCESS_1, m_filePath), 304 ioex); 305 throw ex; 306 } finally { 307 if (reader != null) { 308 try { 309 reader.close(); 310 } catch (IOException e) { 311 LOG.error(e.getLocalizedMessage(), e); 312 } 313 } 314 } 315 } else { 316 return Messages.get().getBundle().key(Messages.GUI_FILE_VIEW_NO_PREVIEW_0); 317 } 318 } 319 320 /** 321 * Sets the additional root folders from which files can be viewed.<p> 322 * 323 * @param roots the list of additional root folders 324 */ 325 public void setAdditionalRoots(List<String> roots) { 326 327 List<String> additionalRoots = new ArrayList<>(); 328 // making sure all paths end with the path separator CHAR 329 for (String path : roots) { 330 if (path != null) { 331 if (!path.endsWith(File.separator)) { 332 path += File.separator; 333 } 334 } 335 additionalRoots.add(path); 336 } 337 m_additionalRoots = additionalRoots; 338 } 339 340 /** 341 * Set the boolean that decides if the view to the underlying file via 342 * <code>{@link #readFilePortion()}</code> is enabled.<p> 343 * 344 * @param preview the boolean that decides if the view to the underlying file via 345 * <code>{@link #readFilePortion()}</code> is enabled 346 */ 347 public void setEnabled(boolean preview) { 348 349 m_enabled = preview; 350 } 351 352 /** 353 * Set the character encoding of the underlying file.<p> 354 * 355 * The given String has to match a valid char set name (canonical or alias) 356 * of one of the system's supported <code>{@link Charset}</code> instances 357 * (see <code>{@link Charset#forName(java.lang.String)}</code>).<p> 358 * 359 * This setting will be used for reading the file. This enables to correctly 360 * display files with text in various encodings in UIs.<p> 361 * 362 * @param fileEncoding the character encoding of the underlying file to set 363 */ 364 public void setFileEncoding(String fileEncoding) { 365 366 checkFrozen(); 367 try { 368 m_fileEncoding = Charset.forName(fileEncoding); 369 } catch (IllegalCharsetNameException icne) { 370 throw new CmsIllegalArgumentException( 371 Messages.get().container(Messages.ERR_CHARSET_ILLEGAL_NAME_1, fileEncoding)); 372 } catch (UnsupportedCharsetException ucse) { 373 throw new CmsIllegalArgumentException( 374 Messages.get().container(Messages.ERR_CHARSET_UNSUPPORTED_1, fileEncoding)); 375 376 } 377 378 } 379 380 /** 381 * Set the path in the real file system that points to the file 382 * that should be displayed.<p> 383 * 384 * This method will only success if the file specified by the <code>path</code> 385 * argument is valid within the file system, no folder and may be read by the 386 * OpenCms process on the current platform.<p> 387 * 388 * @param path the path in the real file system that points to the file that should be displayed to set 389 * 390 * @throws CmsRuntimeException if the configuration of this instance has been frozen 391 * @throws CmsRfsException if the given path is invalid, does not point to a file or cannot be accessed 392 */ 393 public void setFilePath(String path) throws CmsRfsException, CmsRuntimeException { 394 395 checkFrozen(); 396 397 if (path != null) { 398 // leading whitespace from CmsComboWidget causes exception 399 path = path.trim(); 400 } 401 if (CmsStringUtil.isEmpty(path)) { 402 throw new CmsRfsException( 403 Messages.get().container(Messages.ERR_FILE_ARG_EMPTY_1, new Object[] {String.valueOf(path)})); 404 } 405 try { 406 // just for validation : 407 File file = new File(path); 408 if (file.isDirectory()) { 409 // if wrong configuration perform self healing: 410 if (OpenCms.getRunLevel() == OpenCms.RUNLEVEL_2_INITIALIZING) { 411 // this deletes the illegal entry and will default to the log file path 412 m_filePath = null; 413 m_isLogfile = true; 414 } else { 415 throw new CmsRfsException( 416 Messages.get().container( 417 Messages.ERR_FILE_ARG_IS_FOLDER_1, 418 new Object[] {String.valueOf(path)})); 419 } 420 } else if (!file.isFile()) { 421 // if wrong configuration perform self healing: 422 if (OpenCms.getRunLevel() == OpenCms.RUNLEVEL_2_INITIALIZING) { 423 // this deletes the illegal entry and will default to the log file path 424 m_filePath = null; 425 m_isLogfile = true; 426 } else { 427 throw new CmsRfsException( 428 Messages.get().container( 429 Messages.ERR_FILE_ARG_NOT_FOUND_1, 430 new Object[] {String.valueOf(path)})); 431 } 432 433 } else if (!file.canRead()) { 434 // if wrong configuration perform self healing: 435 if (OpenCms.getRunLevel() == OpenCms.RUNLEVEL_2_INITIALIZING) { 436 // this deletes the illegal entry and will default to the log file path 437 m_filePath = null; 438 m_isLogfile = true; 439 } else { 440 throw new CmsRfsException( 441 Messages.get().container( 442 Messages.ERR_FILE_ARG_NOT_READ_1, 443 new Object[] {String.valueOf(path)})); 444 } 445 } else if ((m_rootPath != null) && !isInRoots(file.getCanonicalPath())) { 446 // if wrong configuration perform self healing: 447 if (OpenCms.getRunLevel() == OpenCms.RUNLEVEL_2_INITIALIZING) { 448 // this deletes the illegal entry and will default to the log file path 449 m_filePath = null; 450 m_isLogfile = true; 451 } else { 452 throw new CmsRfsException( 453 Messages.get().container( 454 Messages.ERR_FILE_ARG_NOT_READ_1, 455 new Object[] {String.valueOf(path)})); 456 } 457 } else { 458 m_filePath = file.getCanonicalPath(); 459 } 460 } catch (FileNotFoundException fnfe) { 461 // if wrong configuration perform self healing: 462 if (OpenCms.getRunLevel() == OpenCms.RUNLEVEL_2_INITIALIZING) { 463 // this deletes the illegal entry and will default to the log file path 464 m_filePath = null; 465 m_isLogfile = true; 466 } else { 467 throw new CmsRfsException( 468 Messages.get().container(Messages.ERR_FILE_ARG_NOT_FOUND_1, new Object[] {String.valueOf(path)}), 469 fnfe); 470 } 471 } catch (IOException ioex) { 472 // if wrong configuration perform self healing: 473 if (OpenCms.getRunLevel() == OpenCms.RUNLEVEL_2_INITIALIZING) { 474 // this deletes the illegal entry and will default to the log file path 475 m_filePath = null; 476 m_isLogfile = true; 477 } else { 478 throw new CmsRfsException( 479 Messages.get().container(Messages.ERR_FILE_ARG_ACCESS_1, new Object[] {String.valueOf(path)}), 480 ioex); 481 } 482 483 } 484 } 485 486 /** 487 * Package friendly access that allows the <code>{@link org.opencms.workplace.CmsWorkplaceManager}</code> 488 * to "freeze" this instance within the system-wide assignment in it's 489 * <code>{@link org.opencms.workplace.CmsWorkplaceManager#setFileViewSettings(org.opencms.file.CmsObject, CmsRfsFileViewer)}</code> method.<p> 490 * 491 * @param frozen if true this instance will freeze and throw <code>CmsRuntimeExceptions</code> upon setter invocations 492 * 493 * @throws CmsRuntimeException if the configuration of this instance has been frozen 494 * ({@link #setFrozen(boolean)}) 495 */ 496 public void setFrozen(boolean frozen) throws CmsRuntimeException { 497 498 m_frozen = frozen; 499 } 500 501 /** 502 * Set if the internal file is in standard log file format (true) or not (false).<p> 503 * 504 * If set to true the file might be 505 * treated / displayed in a more convenient format than standard files in future. 506 * Currently it is only inverted (last lines appear first) and only the last 507 * 'Window Size' lines of the file are displayed.<p> 508 * 509 * Do not activate this (it is possible from the log file viewer settings in the workplace 510 * administration) if your selected file is no log file: The display will confuse you and 511 * be more expensive (imaging scrolling a 20 MB file to view the last 200 lines). <p> 512 * 513 * @param isLogfile determines if the internal file is in standard log file format (true) or not (false) 514 * 515 * @throws CmsRuntimeException if the configuration of this instance has been frozen 516 * ({@link #setFrozen(boolean)}) 517 */ 518 public void setIsLogfile(boolean isLogfile) throws CmsRuntimeException { 519 520 checkFrozen(); 521 m_isLogfile = isLogfile; 522 } 523 524 /** 525 * Set the path in the real file system that points to the folder/tree 526 * containing the log files.<p> 527 * 528 * This method will only success if the folder specified by the <code>path</code> 529 * argument is valid within the file system.<p> 530 * 531 * @param path the path in the real file system that points to the folder containing the log files 532 * 533 * @throws CmsRuntimeException if the configuration of this instance has been frozen 534 * @throws CmsRfsException if the given path is invalid 535 */ 536 public void setRootPath(String path) throws CmsRfsException, CmsRuntimeException { 537 538 checkFrozen(); 539 540 if (path != null) { 541 // leading whitespace from CmsComboWidget causes exception 542 path = path.trim(); 543 } 544 if (CmsStringUtil.isEmpty(path)) { 545 throw new CmsRfsException( 546 Messages.get().container(Messages.ERR_FILE_ARG_EMPTY_1, new Object[] {String.valueOf(path)})); 547 } 548 try { 549 // just for validation : 550 File file = new File(path); 551 if (file.exists()) { 552 m_rootPath = file.getCanonicalPath(); 553 } else { 554 // if wrong configuration perform self healing: 555 if (OpenCms.getRunLevel() == OpenCms.RUNLEVEL_2_INITIALIZING) { 556 // this deletes the illegal entry 557 m_rootPath = new File(OpenCms.getSystemInfo().getLogFileRfsPath()).getParent(); 558 } else { 559 560 throw new CmsRfsException( 561 Messages.get().container( 562 Messages.ERR_FILE_ARG_NOT_FOUND_1, 563 new Object[] {String.valueOf(path)})); 564 } 565 } 566 } catch (IOException ioex) { 567 // if wrong configuration perform self healing: 568 if (OpenCms.getRunLevel() == OpenCms.RUNLEVEL_2_INITIALIZING) { 569 // this deletes the illegal entry and will default to the log file path 570 m_rootPath = new File(OpenCms.getSystemInfo().getLogFileRfsPath()).getParent(); 571 } else { 572 573 throw new CmsRfsException( 574 Messages.get().container(Messages.ERR_FILE_ARG_ACCESS_1, new Object[] {String.valueOf(path)}), 575 ioex); 576 } 577 } 578 } 579 580 /** 581 * Sets the start position of the current display.<p> 582 * 583 * This is a count of "windows" that 584 * consist of viewable text with "windowSize" lines of text (for a non-standard log file) or 585 * log-entries (for a standard log file).<p> 586 * 587 * @param windowPos the start position of the current display to set 588 * 589 * @throws CmsRuntimeException if the configuration of this instance has been frozen 590 * ({@link #setFrozen(boolean)}) 591 */ 592 public void setWindowPos(int windowPos) throws CmsRuntimeException { 593 594 checkFrozen(); 595 m_windowPos = windowPos; 596 } 597 598 /** 599 * Set the amount of lines (or entries depending on whether a standard log file is shown) 600 * to display per page.<p> 601 * 602 * @param windowSize the amount of lines to display per page 603 * 604 * @throws CmsRuntimeException if the configuration of this instance has been frozen 605 * ({@link #setFrozen(boolean)}) 606 */ 607 public void setWindowSize(int windowSize) throws CmsRuntimeException { 608 609 checkFrozen(); 610 m_windowSize = windowSize; 611 } 612 613 /** 614 * Internal helper that throws a <code>{@link CmsRuntimeException}</code> if the 615 * configuration of this instance has been frozen ({@link #setFrozen(boolean)}).<p> 616 * 617 * @throws CmsRuntimeException if the configuration of this instance has been frozen 618 * ({@link #setFrozen(boolean)}) 619 */ 620 private void checkFrozen() throws CmsRuntimeException { 621 622 if (m_frozen) { 623 throw new CmsRuntimeException(Messages.get().container(Messages.ERR_FILE_VIEW_SETTINGS_FROZEN_0)); 624 } 625 } 626 627 /** 628 * Reads the log folder RFS path.<p> 629 * 630 * @return the log folder path 631 */ 632 private String getLogFolderPath() { 633 634 String path = null; 635 if (OpenCms.getRunLevel() >= OpenCms.RUNLEVEL_3_SHELL_ACCESS) { 636 path = new File(OpenCms.getSystemInfo().getLogFileRfsPath()).getParent(); 637 // making sure the path ends with the file separator CHAR 638 if ((path != null) && !path.endsWith(File.separator)) { 639 path += File.separator; 640 } 641 642 } 643 return path; 644 } 645 646 /** 647 * Check if the given path is below any of the configured roots.<p> 648 * 649 * @param canonicalPath the path to check 650 * @return true if the path is below any of the configured roots 651 */ 652 private boolean isInRoots(String canonicalPath) { 653 654 if (canonicalPath.startsWith(m_rootPath)) { 655 return true; 656 } 657 if (m_additionalRoots != null) { 658 for (String root : m_additionalRoots) { 659 if (canonicalPath.startsWith(root)) { 660 return true; 661 } 662 663 } 664 } 665 return false; 666 } 667 668 /** 669 * Internally sets the member <code>m_windowPos</code> to the last available 670 * window of <code>m_windowSize</code> windows to let further calls to 671 * <code>{@link #readFilePortion()}</code> display the end of the file. <p> 672 * 673 * This method is triggered when a new file is chosen 674 * (<code>{@link #setFilePath(String)}</code>) because the amount of lines changes. 675 * This method is also triggered when a different window size is chosen 676 * (<code>{@link #setWindowSize(int)}</code>) because the amount of lines to display change. 677 * 678 * @return the amount of lines in the file to view 679 */ 680 private int scrollToFileEnd() { 681 682 int lines = 0; 683 if (OpenCms.getRunLevel() < OpenCms.RUNLEVEL_3_SHELL_ACCESS) { 684 // no scrolling if system not yet fully initialized 685 } else { 686 LineNumberReader reader = null; 687 // shift the window position to the end of the file: this is expensive but OK for ocs logfiles as they 688 // are ltd. to 2 MB 689 try { 690 reader = new LineNumberReader( 691 new BufferedReader(new InputStreamReader(new FileInputStream(m_filePath)))); 692 while (reader.readLine() != null) { 693 lines++; 694 } 695 reader.close(); 696 // if 11.75 windows are available, we don't want to end on window nr. 10 697 int availWindows = (int)Math.ceil((double)lines / (double)m_windowSize); 698 // we start with window 0 699 m_windowPos = availWindows - 1; 700 } catch (IOException ioex) { 701 LOG.error("Unable to scroll file " + m_filePath + " to end. Ensure that it exists. "); 702 } finally { 703 if (reader != null) { 704 try { 705 reader.close(); 706 } catch (Throwable f) { 707 LOG.info("Unable to close reader of file " + m_filePath, f); 708 } 709 } 710 } 711 } 712 return lines; 713 } 714 715}