📄 cmsrfsfileviewer.java
字号:
/*
* 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 + -