All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opencms.util.CmsRfsFileViewer Maven / Gradle / Ivy

Go to download

OpenCms is an enterprise-ready, easy to use website content management system based on Java and XML technology. Offering a complete set of features, OpenCms helps content managers worldwide to create and maintain beautiful websites fast and efficiently.

There is a newer version: 17.0
Show newest version
/*
 * This library is part of OpenCms -
 * the Open Source Content Management System
 *
 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * For further information about Alkacon Software GmbH & Co. KG, please see the
 * company website: http://www.alkacon.com
 *
 * For further information about OpenCms, please see the
 * project website: http://www.opencms.org
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package org.opencms.util;

import org.opencms.i18n.CmsEncoder;
import org.opencms.main.CmsIllegalArgumentException;
import org.opencms.main.CmsLog;
import org.opencms.main.CmsRuntimeException;
import org.opencms.main.OpenCms;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.UnsupportedCharsetException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;

/**
 * The representation of a RFS file along with the settings to provide
 * access to certain portions (amount of lines) of it. 

* * Most often the underlying file will be the OpenCms logfile.

* * The portion of the file that is shown is defined by a "window" of "windowSize" lines of text * at a position "windowPosition" which is an enumeration of windows in ascending order.

* * @since 6.0.0 */ public class CmsRfsFileViewer implements Cloneable { /** The log object for this class. */ protected static final Log LOG = CmsLog.getLog(CmsRfsFileViewer.class); /** The path to the underlying file. */ protected String m_filePath; /** The path to the root for all accessible files. */ protected String m_rootPath; /** The current window (numbered from zero to amount of possible different windows). */ protected int m_windowPos; /** The amount of lines to show. */ protected int m_windowSize; /** The additional allowed RFS roots for viewing files. */ private List m_additionalRoots; /** Decides whether the view onto the underlying file via readFilePortion is enabled. */ private boolean m_enabled; /** The character encoding of the underlying file. */ private Charset m_fileEncoding; /** * If value is true, all setter methods will throw a * {@link CmsRuntimeException}

. * * Only the method {@link #clone()} returns a clone that has set this * member to false allowing modification to take place.

*/ private boolean m_frozen; /** * If true the represented file is a standard OpenCms log file and may be displayed * in more convenient ways (in future versions) because the format is known. */ private boolean m_isLogfile; /** * Creates an instance with default settings that tries to use the log file path obtained * from {@link OpenCms}'s {@link org.opencms.main.CmsSystemInfo} instance.

* * If the log file path is invalid or not configured correctly a logging is performed and the * path remains empty to allow user-specified file selection.

*/ public CmsRfsFileViewer() { m_rootPath = getLogFolderPath(); m_isLogfile = true; // system default charset: see http://java.sun.com/j2se/corejava/intl/reference/faqs/index.html#default-encoding m_fileEncoding = Charset.forName(new OutputStreamWriter(new ByteArrayOutputStream()).getEncoding()); m_enabled = true; m_windowSize = 1000; } /** * Returns a clone of this file view settings that is not "frozen" and therefore allows modifications.

* * Every instance that plans to modify settings has to obtain a clone first that may be * modified. The original instance returned from * ({@link org.opencms.workplace.CmsWorkplaceManager#getFileViewSettings()}) will throw * a {@link CmsRuntimeException} for each setter invocation.

* * @return a clone of this file view settings that is not "frozen" and therefore allows modifications */ @Override public Object clone() { // first run after installation: filePath & rootPath is null: if (m_filePath == null) { // below that runlevel the following call will fail (not initialized from config yet): if (OpenCms.getRunLevel() >= OpenCms.RUNLEVEL_3_SHELL_ACCESS) { m_filePath = OpenCms.getSystemInfo().getLogFileRfsPath(); } } if (m_rootPath == null) { m_rootPath = getLogFolderPath(); } CmsRfsFileViewer clone = new CmsRfsFileViewer(); clone.m_rootPath = m_rootPath; try { // strings are immutable: no outside modification possible. clone.setFilePath(m_filePath); } catch (CmsRfsException e) { // will never happen because m_filePath was verified in setFilePath of this instance. } catch (CmsRuntimeException e) { // will never happen because m_filePath was verified in setFilePath of this instance. } clone.m_fileEncoding = m_fileEncoding; clone.m_isLogfile = m_isLogfile; clone.m_enabled = m_enabled; //clone.m_windowPos = m_windowPos; clone.setWindowSize(m_windowSize); // allow clone-modifications. clone.m_frozen = false; return clone; } /** * Returns the canonical name of the character encoding of the underlying file.

* * If no special choice is fed into * {@link #setFileEncoding(String)} before this call * always the system default character encoding is returned.

* * This value may be ignored outside and will be ignored inside if the * underlying does not contain textual content.

* * @return the canonical name of the character encoding of the underlying file */ public String getFileEncoding() { return m_fileEncoding.name(); } /** * Returns the path denoting the file that is accessed.

* * @return the path denoting the file that is accessed */ public String getFilePath() { return m_filePath; } /** * Returns true if the view's internal file path points to a log file in standard OpenCms format.

* * @return true if the view's internal file path points to a log file in standard OpenCms format */ public boolean getIsLogfile() { // method name is bean-convention of apache.commons.beanutils (unlike eclipse's convention for booleans) return m_isLogfile; } /** * Returns the path denoting the root folder for all accessible files.

* * @return the path denoting the root folder for all accessible files */ public String getRootPath() { return m_rootPath; } /** * Returns the start position of the current display.

* * This is a count of "windows" that * consist of viewable text with "windowSize" lines of text (for a non-standard log file) or * log-entries (for a standard log file).

* * @return the start position of the current display */ public int getWindowPos() { return m_windowPos; } /** * Get the amount of lines (or entries depending on whether a standard log file is shown) * to display per page.

* * @return the amount of lines to display per page */ public int getWindowSize() { return m_windowSize; } /** * Returns true if this view upon the underlying file via * {@link #readFilePortion()} is enabled.

* * * @return true if this view upon the underlying file via * {@link #readFilePortion()} is enabled.

*/ public boolean isEnabled() { return m_enabled; } /** * Return the view portion of lines of text from the underlying file or an * empty String if {@link #isEnabled()} returns false.

* * @return the view portion of lines of text from the underlying file or an * empty String if {@link #isEnabled()} returns false * @throws CmsRfsException if something goes wrong */ public String readFilePortion() throws CmsRfsException { if (m_enabled) { // if we want to view the log file we have to set the internal m_windowPos to the last window // to view the end: int lines = -1; int startLine; if (m_isLogfile) { lines = scrollToFileEnd(); // for logfile mode we show the last window of window size: // it could be possible that only 4 lines are in the last window // (e.g.: 123 lines with windowsize 10 -> last window has 3 lines) // so we ignore the window semantics and show the n last lines: startLine = lines - m_windowSize; } else { m_windowPos = 0; startLine = m_windowPos * m_windowSize; } LineNumberReader reader = null; try { // don't make the buffer too big, just big enough for windowSize lines (estimation: avg. of 200 characters per line) // to save reading too much (this optimizes to read the first windows, much later windows will be slower...) reader = new LineNumberReader( new BufferedReader(new InputStreamReader(new FileInputStream(m_filePath), m_fileEncoding)), m_windowSize * 200); int currentLine = 0; // skip the lines to the current window: while (startLine > currentLine) { reader.readLine(); currentLine++; } StringBuffer result = new StringBuffer(); String read = reader.readLine(); for (int i = m_windowSize; (i > 0) && (read != null); i--) { result.append(read); result.append('\n'); read = reader.readLine(); } return CmsEncoder.escapeXml(result.toString()); } catch (IOException ioex) { CmsRfsException ex = new CmsRfsException( Messages.get().container(Messages.ERR_FILE_ARG_ACCESS_1, m_filePath), ioex); throw ex; } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { LOG.error(e.getLocalizedMessage(), e); } } } } else { return Messages.get().getBundle().key(Messages.GUI_FILE_VIEW_NO_PREVIEW_0); } } /** * Sets the additional root folders from which files can be viewed.

* * @param roots the list of additional root folders */ public void setAdditionalRoots(List roots) { List additionalRoots = new ArrayList<>(); // making sure all paths end with the path separator CHAR for (String path : roots) { if (path != null) { if (!path.endsWith(File.separator)) { path += File.separator; } } additionalRoots.add(path); } m_additionalRoots = additionalRoots; } /** * Set the boolean that decides if the view to the underlying file via * {@link #readFilePortion()} is enabled.

* * @param preview the boolean that decides if the view to the underlying file via * {@link #readFilePortion()} is enabled */ public void setEnabled(boolean preview) { m_enabled = preview; } /** * Set the character encoding of the underlying file.

* * The given String has to match a valid char set name (canonical or alias) * of one of the system's supported {@link Charset} instances * (see {@link Charset#forName(java.lang.String)}).

* * This setting will be used for reading the file. This enables to correctly * display files with text in various encodings in UIs.

* * @param fileEncoding the character encoding of the underlying file to set */ public void setFileEncoding(String fileEncoding) { checkFrozen(); try { m_fileEncoding = Charset.forName(fileEncoding); } catch (IllegalCharsetNameException icne) { throw new CmsIllegalArgumentException( Messages.get().container(Messages.ERR_CHARSET_ILLEGAL_NAME_1, fileEncoding)); } catch (UnsupportedCharsetException ucse) { throw new CmsIllegalArgumentException( Messages.get().container(Messages.ERR_CHARSET_UNSUPPORTED_1, fileEncoding)); } } /** * Set the path in the real file system that points to the file * that should be displayed.

* * This method will only success if the file specified by the path * argument is valid within the file system, no folder and may be read by the * OpenCms process on the current platform.

* * @param path the path in the real file system that points to the file that should be displayed to set * * @throws CmsRuntimeException if the configuration of this instance has been frozen * @throws CmsRfsException if the given path is invalid, does not point to a file or cannot be accessed */ public void setFilePath(String path) throws CmsRfsException, CmsRuntimeException { checkFrozen(); if (path != null) { // leading whitespace from CmsComboWidget causes exception path = path.trim(); } if (CmsStringUtil.isEmpty(path)) { throw new CmsRfsException( Messages.get().container(Messages.ERR_FILE_ARG_EMPTY_1, new Object[] {String.valueOf(path)})); } try { // just for validation : File file = new File(path); if (file.isDirectory()) { // if wrong configuration perform self healing: if (OpenCms.getRunLevel() == OpenCms.RUNLEVEL_2_INITIALIZING) { // this deletes the illegal entry and will default to the log file path m_filePath = null; m_isLogfile = true; } else { throw new CmsRfsException( Messages.get().container( Messages.ERR_FILE_ARG_IS_FOLDER_1, new Object[] {String.valueOf(path)})); } } else if (!file.isFile()) { // if wrong configuration perform self healing: if (OpenCms.getRunLevel() == OpenCms.RUNLEVEL_2_INITIALIZING) { // this deletes the illegal entry and will default to the log file path m_filePath = null; m_isLogfile = true; } else { throw new CmsRfsException( Messages.get().container( Messages.ERR_FILE_ARG_NOT_FOUND_1, new Object[] {String.valueOf(path)})); } } else if (!file.canRead()) { // if wrong configuration perform self healing: if (OpenCms.getRunLevel() == OpenCms.RUNLEVEL_2_INITIALIZING) { // this deletes the illegal entry and will default to the log file path m_filePath = null; m_isLogfile = true; } else { throw new CmsRfsException( Messages.get().container( Messages.ERR_FILE_ARG_NOT_READ_1, new Object[] {String.valueOf(path)})); } } else if ((m_rootPath != null) && !isInRoots(file.getCanonicalPath())) { // if wrong configuration perform self healing: if (OpenCms.getRunLevel() == OpenCms.RUNLEVEL_2_INITIALIZING) { // this deletes the illegal entry and will default to the log file path m_filePath = null; m_isLogfile = true; } else { throw new CmsRfsException( Messages.get().container( Messages.ERR_FILE_ARG_NOT_READ_1, new Object[] {String.valueOf(path)})); } } else { m_filePath = file.getCanonicalPath(); } } catch (FileNotFoundException fnfe) { // if wrong configuration perform self healing: if (OpenCms.getRunLevel() == OpenCms.RUNLEVEL_2_INITIALIZING) { // this deletes the illegal entry and will default to the log file path m_filePath = null; m_isLogfile = true; } else { throw new CmsRfsException( Messages.get().container(Messages.ERR_FILE_ARG_NOT_FOUND_1, new Object[] {String.valueOf(path)}), fnfe); } } catch (IOException ioex) { // if wrong configuration perform self healing: if (OpenCms.getRunLevel() == OpenCms.RUNLEVEL_2_INITIALIZING) { // this deletes the illegal entry and will default to the log file path m_filePath = null; m_isLogfile = true; } else { throw new CmsRfsException( Messages.get().container(Messages.ERR_FILE_ARG_ACCESS_1, new Object[] {String.valueOf(path)}), ioex); } } } /** * Package friendly access that allows the {@link org.opencms.workplace.CmsWorkplaceManager} * to "freeze" this instance within the system-wide assignment in it's * {@link org.opencms.workplace.CmsWorkplaceManager#setFileViewSettings(org.opencms.file.CmsObject, CmsRfsFileViewer)} method.

* * @param frozen if true this instance will freeze and throw CmsRuntimeExceptions upon setter invocations * * @throws CmsRuntimeException if the configuration of this instance has been frozen * ({@link #setFrozen(boolean)}) */ public void setFrozen(boolean frozen) throws CmsRuntimeException { m_frozen = frozen; } /** * Set if the internal file is in standard log file format (true) or not (false).

* * If set to true the file might be * treated / displayed in a more convenient format than standard files in future. * Currently it is only inverted (last lines appear first) and only the last * 'Window Size' lines of the file are displayed.

* * Do not activate this (it is possible from the log file viewer settings in the workplace * administration) if your selected file is no log file: The display will confuse you and * be more expensive (imaging scrolling a 20 MB file to view the last 200 lines).

* * @param isLogfile determines if the internal file is in standard log file format (true) or not (false) * * @throws CmsRuntimeException if the configuration of this instance has been frozen * ({@link #setFrozen(boolean)}) */ public void setIsLogfile(boolean isLogfile) throws CmsRuntimeException { checkFrozen(); m_isLogfile = isLogfile; } /** * Set the path in the real file system that points to the folder/tree * containing the log files.

* * This method will only success if the folder specified by the path * argument is valid within the file system.

* * @param path the path in the real file system that points to the folder containing the log files * * @throws CmsRuntimeException if the configuration of this instance has been frozen * @throws CmsRfsException if the given path is invalid */ public void setRootPath(String path) throws CmsRfsException, CmsRuntimeException { checkFrozen(); if (path != null) { // leading whitespace from CmsComboWidget causes exception path = path.trim(); } if (CmsStringUtil.isEmpty(path)) { throw new CmsRfsException( Messages.get().container(Messages.ERR_FILE_ARG_EMPTY_1, new Object[] {String.valueOf(path)})); } try { // just for validation : File file = new File(path); if (file.exists()) { m_rootPath = file.getCanonicalPath(); } else { // if wrong configuration perform self healing: if (OpenCms.getRunLevel() == OpenCms.RUNLEVEL_2_INITIALIZING) { // this deletes the illegal entry m_rootPath = new File(OpenCms.getSystemInfo().getLogFileRfsPath()).getParent(); } else { throw new CmsRfsException( Messages.get().container( Messages.ERR_FILE_ARG_NOT_FOUND_1, new Object[] {String.valueOf(path)})); } } } catch (IOException ioex) { // if wrong configuration perform self healing: if (OpenCms.getRunLevel() == OpenCms.RUNLEVEL_2_INITIALIZING) { // this deletes the illegal entry and will default to the log file path m_rootPath = new File(OpenCms.getSystemInfo().getLogFileRfsPath()).getParent(); } else { throw new CmsRfsException( Messages.get().container(Messages.ERR_FILE_ARG_ACCESS_1, new Object[] {String.valueOf(path)}), ioex); } } } /** * Sets the start position of the current display.

* * This is a count of "windows" that * consist of viewable text with "windowSize" lines of text (for a non-standard log file) or * log-entries (for a standard log file).

* * @param windowPos the start position of the current display to set * * @throws CmsRuntimeException if the configuration of this instance has been frozen * ({@link #setFrozen(boolean)}) */ public void setWindowPos(int windowPos) throws CmsRuntimeException { checkFrozen(); m_windowPos = windowPos; } /** * Set the amount of lines (or entries depending on whether a standard log file is shown) * to display per page.

* * @param windowSize the amount of lines to display per page * * @throws CmsRuntimeException if the configuration of this instance has been frozen * ({@link #setFrozen(boolean)}) */ public void setWindowSize(int windowSize) throws CmsRuntimeException { checkFrozen(); m_windowSize = windowSize; } /** * Internal helper that throws a {@link CmsRuntimeException} if the * configuration of this instance has been frozen ({@link #setFrozen(boolean)}).

* * @throws CmsRuntimeException if the configuration of this instance has been frozen * ({@link #setFrozen(boolean)}) */ private void checkFrozen() throws CmsRuntimeException { if (m_frozen) { throw new CmsRuntimeException(Messages.get().container(Messages.ERR_FILE_VIEW_SETTINGS_FROZEN_0)); } } /** * Reads the log folder RFS path.

* * @return the log folder path */ private String getLogFolderPath() { String path = null; if (OpenCms.getRunLevel() >= OpenCms.RUNLEVEL_3_SHELL_ACCESS) { path = new File(OpenCms.getSystemInfo().getLogFileRfsPath()).getParent(); // making sure the path ends with the file separator CHAR if ((path != null) && !path.endsWith(File.separator)) { path += File.separator; } } return path; } /** * Check if the given path is below any of the configured roots.

* * @param canonicalPath the path to check * @return true if the path is below any of the configured roots */ private boolean isInRoots(String canonicalPath) { if (canonicalPath.startsWith(m_rootPath)) { return true; } if (m_additionalRoots != null) { for (String root : m_additionalRoots) { if (canonicalPath.startsWith(root)) { return true; } } } return false; } /** * Internally sets the member m_windowPos to the last available * window of m_windowSize windows to let further calls to * {@link #readFilePortion()} display the end of the file.

* * This method is triggered when a new file is chosen * ({@link #setFilePath(String)}) because the amount of lines changes. * This method is also triggered when a different window size is chosen * ({@link #setWindowSize(int)}) because the amount of lines to display change. * * @return the amount of lines in the file to view */ private int scrollToFileEnd() { int lines = 0; if (OpenCms.getRunLevel() < OpenCms.RUNLEVEL_3_SHELL_ACCESS) { // no scrolling if system not yet fully initialized } else { LineNumberReader reader = null; // shift the window position to the end of the file: this is expensive but OK for ocs logfiles as they // are ltd. to 2 MB try { reader = new LineNumberReader( new BufferedReader(new InputStreamReader(new FileInputStream(m_filePath)))); while (reader.readLine() != null) { lines++; } reader.close(); // if 11.75 windows are available, we don't want to end on window nr. 10 int availWindows = (int)Math.ceil((double)lines / (double)m_windowSize); // we start with window 0 m_windowPos = availWindows - 1; } catch (IOException ioex) { LOG.error("Unable to scroll file " + m_filePath + " to end. Ensure that it exists. "); } finally { if (reader != null) { try { reader.close(); } catch (Throwable f) { LOG.info("Unable to close reader of file " + m_filePath, f); } } } } return lines; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy