⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cmsrfsfileviewer.java

📁 cms是开源的框架
💻 JAVA
📖 第 1 页 / 共 3 页
字号:
/*
 * File   : $Source: /usr/local/cvs/opencms/src/org/opencms/util/CmsRfsFileViewer.java,v $
 * Date   : $Date: 2006/03/27 14:52:41 $
 * Version: $Revision: 1.17 $
 *
 * This library is part of OpenCms -
 * the Open Source Content Mananagement System
 *
 * Copyright (c) 2005 Alkacon Software GmbH (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, 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.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.UnsupportedCharsetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Stack;

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. <p> 
 *  
 * Most often the underlying file will be the OpenCms logfile. <p>
 * 
 * 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. <p>
 * 
 * @author  Achim Westermann 
 * 
 * @version $Revision: 1.17 $ 
 * 
 * @since 6.0.0 
 */
public class CmsRfsFileViewer implements Cloneable {

    /**
     * An index for a file from line numbers to character positions 
     * in the underlying streams of {@link Reader} implementations.<p> 
     * 
     * It enables to skip big parts of the underlying file when trying to access 
     * file content at later line numbers of a file. <p>
     * 
     */
    private final class CmsRfsFileLineIndexInfo {

        /**
         * The <code>{@link System#currentTimeMillis()}</code> taken when the 
         * index was created.<p>
         */
        protected long m_creationTime;

        /**
         * The timespan in milliseconds after that an line number index for a file will expire. <p>
         * 
         * This may be set per file: A log file will update faster (index expires) than a configuration 
         * file. <p>
         * 
         * Default corresponds to 30 seconds. <p>
         * 
         */
        protected int m_maxIndexAge = 60000;

        /**
         * The internal list with break positions in bytes.<p>
         * 
         * Fast for random access which is 
         * used for the query for the n<sup>th</sup> line break.<p>
         */
        private List m_breakPositions;

        /** The file that is indexed. */
        private File m_file;

        /**
         * Constructor for a line number index to build for the current underlying file 
         * of the outer class.<p>
         * 
         * @param toIndex the file to build the index for. 
         * 
         */
        protected CmsRfsFileLineIndexInfo(File toIndex) {

            // enough space for indexing 2000 lines without re-malloc, will be dropped if expired. 
            m_breakPositions = new ArrayList(2000);
            m_file = toIndex;
            rebuildIndex();

        }

        /**
         * Log reporting of freed index.<p>
         * 
         * Do not invoke explicitly or the log reader gets confused. 
         * As this method is invoked by the garbage collector without any 
         * guarantee of runtime behaviour nothing important is done here. Just 
         * logging to see that the mechanism of dropping indices works correctly.<p>
         * 
         * @see java.lang.Object#finalize()
         */
        public void finalize() throws Throwable {

            super.finalize();
            // switch to info!
            if (LOG.isDebugEnabled()) {
                // sizeof Long is 16 Bytes
                LOG.debug(Messages.get().getBundle().key(
                    Messages.LOG_FILEVIEW_INDEX_EXPIRE_OK_2,
                    CmsFileUtil.formatFilesize(m_breakPositions.size() * 16, Locale.getDefault()),
                    m_file.getAbsolutePath()));
            }
        }

        /**
         * Returns the <code>{@link System#currentTimeMillis()}</code> taken when the 
         * index was created.<p>
         *  
         * @return the <code>{@link System#currentTimeMillis()}</code> taken when the 
         * index was created.
         */
        public long getCreationTime() {

            return m_creationTime;
        }

        /**
         * Returns the file offset to position zero in characters of this break or 
         * offset to the character position where the last 10 lines begin if the 
         * requested linebreak is higher than the amount of lines in the underlying file.<p>
         * 
         * @param breakNumber the number defining that this break position marks the  n<sup>th</sup> break 
         *        in the underlying file.  
         * @param linesToRead the number of lines that are desired to be read. This allows 
         *        to shift the break number in case the position is at the file end. 
         * 
         * @return the file offset to position zero in characters of this break or 
         *         offset to the character position where the last 10 lines begin if the 
         *         requested linebreak is higher than the amount of lines in the underlying file
         */
        public long getLineBreakPosition(int breakNumber, int linesToRead) {

            // using m_windowSize instead of param linesToRead would be more elegant 
            // but was impossible because of the workplace clone() - security idiom: 
            // the shifted CmsRfsFileLineIndexInfo instances would access wrong value.
            int sz = m_breakPositions.size();

            if (breakNumber <= sz) {
                // for the last window don't take it too square with the window position 
                // (which is a computed arg to this method: m_windowPos*m_windowSize): 
                // Display a full window: 

                if (sz - breakNumber < linesToRead) {
                    // more would fit into the last window.
                    breakNumber = sz - linesToRead;
                    if (breakNumber < 0) {
                        // window is bigger than lines in file
                        breakNumber = 0;
                    }
                }
                // valid number of line...
                return ((Number)m_breakPositions.get(breakNumber)).longValue();
            } else {
                // out of range: don't throw exception, return last window lines.
                if (m_windowSize > sz) {
                    breakNumber = sz - linesToRead;
                } else {
                    breakNumber = 0;
                }
                return ((Number)m_breakPositions.get(breakNumber)).longValue();
            }
        }

        /**
         * Returns the maximum age in milliseconds this line number index may have.<p> 
         * 
         * @return the maximum age in milliseconds this line number index may have
         */

        public int getMaxIndexAge() {

            return m_maxIndexAge;
        }

        /**
         * Returns the amount of lines of text available from the underlying file.<p> 
         * 
         * This method performs fast because it already has access to the line number 
         * index. <p> 
         * 
         * @return the amount of lines of text available from the underlying file.
         */
        public int lineCount() {

            return m_breakPositions.size();
        }

        /**
         * Set the maximum age in milliseconds this line number index may have.<p> 
         * 
         * @param maxIndexAge the maximum age in milliseconds this line number index may have to set 
         */
        public void setMaxIndexAge(int maxIndexAge) {

            m_maxIndexAge = maxIndexAge;
        }

        /**
         * Adds a further line break position to the internal <code>List</code> that marks 
         * the "list.size()<sup>th</sup>" line break at position <code>breakPosition</code>.<p>
         * 
         * As a result of this policy the order of calls to this method has to be synchronized with ascending 
         * break positions. Therefore this mehod is private and only intended for an internal line indexing Thread.<p> 
         * 
         * @param breakPosition a position in the character stream (<code>{@link Reader}</code>) where the lates line 
         *                      break was found. 
         */
        protected void addLineBreakPosition(long breakPosition) {

            synchronized (m_breakPositions) {
                m_breakPositions.add(new Long(breakPosition));
            }
        }

        /**
         * Starts index - creation by a Thread. <p>
         * 
         * The old index is completely dropped as over time a file may become 
         * not only longer but also shorter (e.g. log rotation). <p>
         */
        private void rebuildIndex() {

            m_breakPositions.clear();
            // first line at 0.
            addLineBreakPosition(0);
            LineNumberReader reader = null;
            m_creationTime = System.currentTimeMillis();
            try {
                InputStreamCounter streamPeeker = new InputStreamCounter(new FileInputStream(m_file));
                reader = new LineNumberReader(streamPeeker, 1);
                while (reader.readLine() != null) {
                    CmsRfsFileLineIndexInfo.this.addLineBreakPosition(streamPeeker.position());
                    synchronized (CmsRfsFileLineIndexInfo.this) {
                        CmsRfsFileLineIndexInfo.this.notify();
                    }
                }

            } catch (IOException e) {
                LOG.error(e.toString());
            } finally {
                if (reader != null) {
                    try {
                        reader.close();
                    } catch (IOException e1) {
                        LOG.error(e1);
                    }
                }
            }
        }
    }

    /**
     * 
     * An internal <code>InputStreamReader</code> that proxies a "real" 
     * <code>InputStreamReader</code> and counts the chars read. <p> 
     * 
     * This allows counting characters even if some higher io decorator is 
     * used. For example a <code>{@link LineNumberReader}</code> would allow 
     * to count the characters of the lines to be read from outside but not the 
     * line delimiters which are file or system dependant.<p>
     * 
     * <b>Do not decorate instances with <code>{@link java.io.BufferedReader}</code> 
     * unless you specify a buffer size for it's constructor.</b>
     * Those instances will internally read big portions to cache before the 
     * outside using code has read it. <br>
     * Remember that a <code>{@link LineNumberedReader}</code> is a <code>BufferedReader</code>: 
     * use the constructor specifying the buffer size with value '1'.<p>
     * 
     * @author Achim Westermann
     * 
     * @version $Revision: 1.17 $
     * 
     * @since 6.0.0
     */
    private class InputStreamCounter extends InputStreamReader {

        /** the delegating object. */
        private InputStreamReader m_delegate;

        /** current position. */
        private long m_position;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -