📄 cmsflexresponse.java
字号:
CmsFlexCacheKey getCmsCacheKey() {
return m_key;
}
/**
* Is used to check if the response has an include list,
* which indicates a) it is probalbly processing a JSP element
* and b) it can never be streamed and alwys must be buffered.<p>
*
* @return true if this response has an include list, false otherwise
*/
boolean hasIncludeList() {
return m_includeList != null;
}
/**
* Generates a CmsFlexCacheEntry from the current response using the
* stored include results.<p>
*
* In case the results were written only to the buffer until now,
* they are now re-written on the output stream, with all included
* elements.<p>
*
* @throws IOException tn case something goes wrong while writing to the output stream
* @return the generated cache entry
*/
CmsFlexCacheEntry processCacheEntry() throws IOException {
if (isSuspended() && (m_bufferRedirect == null)) {
// an included element redirected this response, no cache entry must be produced
return null;
}
if (m_cachingRequired) {
// cache entry must only be calculated if it's actually needed (always true if we write only to buffer)
m_cachedEntry = new CmsFlexCacheEntry();
if (m_bufferRedirect != null) {
// only set et cached redirect target
m_cachedEntry.setRedirect(m_bufferRedirect);
} else {
// add cached headers
m_cachedEntry.addHeaders(m_bufferHeaders);
// add cached output
if (m_includeList != null) {
// probably JSP: we must analyze out stream for includes calls
// also, m_writeOnlyToBuffer must be "true" or m_includeList can not be != null
processIncludeList();
} else {
// output is delivered directly, no include call parsing required
m_cachedEntry.add(getWriterBytes());
}
}
// update the "last modified" date for the cache entry
m_cachedEntry.complete();
}
// in case the output was only bufferd we have to re-write it to the "right" stream
if (m_writeOnlyToBuffer) {
// since we are processing a cache entry caching is not required
m_cachingRequired = false;
if (m_bufferRedirect != null) {
// send buffered redirect, will trigger redirect of top response
sendRedirect(m_bufferRedirect);
} else {
// process the output
if (m_parentWritesOnlyToBuffer) {
// write results back to own stream, headers are already in buffer
if (m_out != null) {
try {
m_out.clear();
} catch (Exception e) {
if (LOG.isDebugEnabled()) {
LOG.debug(Messages.get().getBundle().key(
Messages.LOG_FLEXRESPONSE_ERROR_FLUSHING_OUTPUT_STREAM_1,
e));
}
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug(Messages.get().getBundle().key(
Messages.LOG_FLEXRESPONSE_ERROR_OUTPUT_STREAM_NULL_0));
}
}
writeCachedResultToStream(this);
} else {
// we can use the parent stream
processHeaders(m_headers, m_res);
writeCachedResultToStream(m_res);
}
}
}
return m_cachedEntry;
}
/**
* Sets the cache key for this response from
* a pre-calculated cache key.<p>
*
* @param value the cache key to set
*/
void setCmsCacheKey(CmsFlexCacheKey value) {
m_key = value;
}
/**
* Sets the cache key for this response, which is calculated
* from the provided parameters.<p>
*
* @param resourcename the target resouce for which to create the cache key
* @param cacheDirectives the cache directives of the resource (value of the property "cache")
* @param online indicates if this resource is online or offline
*
* @return the generated cache key
* @throws CmsFlexCacheException in case the value String had a parse error
*/
CmsFlexCacheKey setCmsCacheKey(String resourcename, String cacheDirectives, boolean online)
throws CmsFlexCacheException {
m_key = new CmsFlexCacheKey(resourcename, cacheDirectives, online);
if (m_key.hadParseError()) {
// We throw the exception here to make sure this response has a valid key (cache=never)
throw new CmsFlexCacheException(Messages.get().container(
Messages.LOG_FLEXRESPONSE_PARSE_ERROR_IN_CACHE_KEY_2,
cacheDirectives,
resourcename));
}
return m_key;
}
/**
* Set caching status for this reponse.<p>
*
* Will always be set to <code>"true"</code> if setOnlyBuffering() is set to <code>"true"</code>.
* Currently this is an optimization for non - JSP elements that
* are known not to be cachable.<p>
*
* @param value the value to set
*/
void setCmsCachingRequired(boolean value) {
m_cachingRequired = (value || m_writeOnlyToBuffer) && !m_controller.isForwardMode();
}
/**
* This flag indicates to the response if it is in "include mode" or not.<p>
*
* This is important in case a cache entry is constructed,
* since the cache entry must not consist of output or headers of the
* included elements.<p>
*
* @param value the value to set
*/
void setCmsIncludeMode(boolean value) {
m_includeMode = value;
}
/**
* Sets the suspended status of the response, and also sets
* the suspend status of all responses wrapping this response.<p>
*
* A suspended response mut not write further output to any stream or
* process a cache entry for itself.<p>
*
* @param value the value to set
*/
void setSuspended(boolean value) {
m_suspended = value;
}
/**
* Writes some bytes to the current output stream,
* this method should be called from CmsFlexCacheEntry.service() only.<p>
*
* @param bytes an array of bytes
* @param useArray indicates that the byte array should be used directly
* @throws IOException in case something goes wrong while writing to the stream
*/
void writeToOutputStream(byte[] bytes, boolean useArray) throws IOException {
if (isSuspended()) {
return;
}
if (m_writeOnlyToBuffer) {
if (useArray) {
// This cached entry has no sub-elements (it a "leaf") and so we can just use it's bytes
m_cacheBytes = bytes;
} else {
if (m_out == null) {
initStream();
}
// In this case the buffer will not write to the servlet stream, but to it's internal buffer only
m_out.write(bytes);
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug(Messages.get().getBundle().key(Messages.LOG_FLEXRESPONSE_ERROR_WRITING_TO_OUTPUT_STREAM_0));
}
// The request is not buffered, so we can write directly to it's parents output stream
m_res.getOutputStream().write(bytes);
m_res.getOutputStream().flush();
}
}
/**
* Helper method to add a value in the internal header list.<p>
*
* @param headers the headers to look up the value in
* @param name the name to look up
* @param value the value to set
*/
private void addHeaderList(Map headers, String name, String value) {
ArrayList values = (ArrayList)headers.get(name);
if (values == null) {
values = new ArrayList();
headers.put(name, values);
}
values.add(value);
}
/**
* Initializes the current responses output stream
* and the corrosponding print writer.<p>
*
* @throws IOException in case something goes wrong while initializing
*/
private void initStream() throws IOException {
if (m_out == null) {
if (!m_writeOnlyToBuffer) {
// we can use the parents output stream
if (m_cachingRequired || (m_controller.getResponseStackSize() > 1)) {
// we are allowed to cache our results (probably to contruct a new cache entry)
m_out = new CmsFlexResponse.CmsServletOutputStream(m_res.getOutputStream());
} else {
// we are not allowed to cache so we just use the parents output stream
m_out = (CmsFlexResponse.CmsServletOutputStream)m_res.getOutputStream();
}
} else {
// construct a "buffer only" output stream
m_out = new CmsFlexResponse.CmsServletOutputStream();
}
}
if (m_writer == null) {
// create a PrintWriter that uses the encoding required for the request context
m_writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(m_out, m_encoding)), false);
}
}
/**
* This method is needed to process pages that can NOT be analyzed
* directly during delivering (like JSP) because they write to
* their own buffer.<p>
*
* In this case, we don't actually write output of include calls to the stream.
* Where there are include calls we write a <code>{@link #FLEX_CACHE_DELIMITER}</code> char on the stream
* to indicate that at this point the output of the include must be placed later.
* The include targets (resource names) are then saved in the m_includeList.<p>
*
* This method must be called after the complete page has been processed.
* It will contain the output of the page only (no includes),
* with <code>{@link #FLEX_CACHE_DELIMITER}</code> chars were the include calls should be placed.
* What we do here is analyze the output and cut it in parts
* of <code>byte[]</code> arrays which then are saved in the resulting cache entry.
* For the includes, we just save the name of the resource in
* the cache entry.<p>
*
* If caching is disabled this method is just not called.<p>
*/
private void processIncludeList() {
byte[] result = getWriterBytes();
if (!hasIncludeList()) {
// no include list, so no includes and we just use the bytes as they are in one block
m_cachedEntry.add(result);
result = null;
} else {
// process the include list
int max = result.length;
int pos = 0;
int last = 0;
int size = 0;
int count = 0;
// work through result and split this with include list calls
int i = 0;
int j = 0;
while (i < m_includeList.size() && (pos < max)) {
// look for the first FLEX_CACHE_DELIMITER char
while ((pos < max) && (result[pos] != FLEX_CACHE_DELIMITER)) {
pos++;
}
if ((pos < max) && (result[pos] == FLEX_CACHE_DELIMITER)) {
count++;
// a byte value of C_FLEX_CACHE_DELIMITER in our (String) output list indicates
// that the next include call must be placed here
size = pos - last;
if (size > 0) {
// if not (it might be 0) there would be 2 include calls back 2 back
byte[] piece = new byte[size];
System.arraycopy(result, last, piece, 0, size);
// add the byte array to the cache entry
m_cachedEntry.add(piece);
piece = null;
}
last = ++pos;
// add an include call to the cache entry
m_cachedEntry.add((String)m_includeList.get(i++), (Map)m_includeListParameters.get(j++));
}
}
if (pos < max) {
// there is content behind the last include call
size = max - pos;
byte[] piece = new byte[size];
System.arraycopy(result, pos, piece, 0, size);
m_cachedEntry.add(piece);
piece = null;
}
result = null;
if (i >= m_includeList.size()) {
// clear the include list if all include calls are handled
m_includeList = null;
m_includeListParameters = null;
} else {
// if something is left, remove the processed entries
m_includeList = m_includeList.subList(count, m_includeList.size());
m_includeListParameters = m_includeListParameters.subList(count, m_includeListParameters.size());
}
}
}
/**
* Helper method to set a value in the internal header list.
*
* @param headers the headers to set the value in
* @param name the name to set
* @param value the value to set
*/
private void setHeaderList(Map headers, String name, String value) {
ArrayList values = new ArrayList();
values.add(SET_HEADER + value);
headers.put(name, values);
}
/**
* This delivers cached sub-elements back to the stream.
* Needed to overcome JSP buffering.<p>
*
* @param res the response to write the cached results to
* @throws IOException in case something goes wrong writing to the responses output stream
*/
private void writeCachedResultToStream(HttpServletResponse res) throws IOException {
List elements = m_cachedEntry.elements();
int count = 0;
if (elements != null) {
for (int i = 0; i < elements.size(); i++) {
Object o = elements.get(i);
if (o instanceof byte[]) {
res.getOutputStream().write((byte[])o);
} else {
if ((m_includeResults != null) && (m_includeResults.size() > count)) {
// make sure that we don't run behind end of list (should never happen, though)
res.getOutputStream().write((byte[])m_includeResults.get(count));
count++;
}
// skip next entry, which is the parameter list for this incluce call
i++;
}
}
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -