blob: 7f9f03b75df48133e34e0ba33815967d0a34f3ae [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* $Id$
*/
package org.apache.xalan.xsltc.dom;
import java.io.File;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.util.Date;
import java.util.Hashtable;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.TransformerException;
import javax.xml.transform.sax.SAXSource;
import org.apache.xalan.xsltc.DOM;
import org.apache.xalan.xsltc.DOMCache;
import org.apache.xalan.xsltc.DOMEnhancedForDTM;
import org.apache.xalan.xsltc.Translet;
import org.apache.xalan.xsltc.runtime.AbstractTranslet;
import org.apache.xalan.xsltc.runtime.BasisLibrary;
import org.apache.xalan.xsltc.runtime.Constants;
import org.apache.xml.utils.SystemIDResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
/**
* @author Morten Jorgensen
*/
public final class DocumentCache implements DOMCache {
private int _size;
private Hashtable _references;
private String[] _URIs;
private int _count;
private int _current;
private SAXParser _parser;
private XMLReader _reader;
private XSLTCDTMManager _dtmManager;
private static final int REFRESH_INTERVAL = 1000;
/*
* Inner class containing a DOMImpl object and DTD handler
*/
public final class CachedDocument {
// Statistics data
private long _firstReferenced;
private long _lastReferenced;
private long _accessCount;
private long _lastModified;
private long _lastChecked;
private long _buildTime;
// DOM and DTD handler references
private DOMEnhancedForDTM _dom = null;
/**
* Constructor - load document and initialise statistics
*/
public CachedDocument(String uri) {
// Initialise statistics variables
final long stamp = System.currentTimeMillis();
_firstReferenced = stamp;
_lastReferenced = stamp;
_accessCount = 0;
loadDocument(uri);
_buildTime = System.currentTimeMillis() - stamp;
}
/**
* Loads the document and updates build-time (latency) statistics
*/
public void loadDocument(String uri) {
try {
final long stamp = System.currentTimeMillis();
_dom = (DOMEnhancedForDTM)_dtmManager.getDTM(
new SAXSource(_reader, new InputSource(uri)),
false, null, true, false);
_dom.setDocumentURI(uri);
// The build time can be used for statistics for a better
// priority algorithm (currently round robin).
final long thisTime = System.currentTimeMillis() - stamp;
if (_buildTime > 0)
_buildTime = (_buildTime + thisTime) >>> 1;
else
_buildTime = thisTime;
}
catch (Exception e) {
_dom = null;
}
}
public DOM getDocument() { return(_dom); }
public long getFirstReferenced() { return(_firstReferenced); }
public long getLastReferenced() { return(_lastReferenced); }
public long getAccessCount() { return(_accessCount); }
public void incAccessCount() { _accessCount++; }
public long getLastModified() { return(_lastModified); }
public void setLastModified(long t){ _lastModified = t; }
public long getLatency() { return(_buildTime); }
public long getLastChecked() { return(_lastChecked); }
public void setLastChecked(long t) { _lastChecked = t; }
public long getEstimatedSize() {
if (_dom != null)
return(_dom.getSize() << 5); // ???
else
return(0);
}
}
/**
* DocumentCache constructor
*/
public DocumentCache(int size) throws SAXException {
this(size, null);
try {
_dtmManager = (XSLTCDTMManager)XSLTCDTMManager.getDTMManagerClass()
.newInstance();
} catch (Exception e) {
throw new SAXException(e);
}
}
/**
* DocumentCache constructor
*/
public DocumentCache(int size, XSLTCDTMManager dtmManager) throws SAXException {
_dtmManager = dtmManager;
_count = 0;
_current = 0;
_size = size;
_references = new Hashtable(_size+2);
_URIs = new String[_size];
try {
// Create a SAX parser and get the XMLReader object it uses
final SAXParserFactory factory = SAXParserFactory.newInstance();
try {
factory.setFeature(Constants.NAMESPACE_FEATURE,true);
}
catch (Exception e) {
factory.setNamespaceAware(true);
}
_parser = factory.newSAXParser();
_reader = _parser.getXMLReader();
}
catch (ParserConfigurationException e) {
BasisLibrary.runTimeError(BasisLibrary.NAMESPACES_SUPPORT_ERR);
}
}
/**
* Returns the time-stamp for a document's last update
*/
private final long getLastModified(String uri) {
try {
URL url = new URL(uri);
URLConnection connection = url.openConnection();
long timestamp = connection.getLastModified();
// Check for a "file:" URI (courtesy of Brian Ewins)
if (timestamp == 0){ // get 0 for local URI
if ("file".equals(url.getProtocol())){
File localfile = new File(URLDecoder.decode(url.getFile()));
timestamp = localfile.lastModified();
}
}
return(timestamp);
}
// Brutal handling of all exceptions
catch (Exception e) {
return(System.currentTimeMillis());
}
}
/**
*
*/
private CachedDocument lookupDocument(String uri) {
return((CachedDocument)_references.get(uri));
}
/**
*
*/
private synchronized void insertDocument(String uri, CachedDocument doc) {
if (_count < _size) {
// Insert out URI in circular buffer
_URIs[_count++] = uri;
_current = 0;
}
else {
// Remove oldest URI from reference Hashtable
_references.remove(_URIs[_current]);
// Insert our URI in circular buffer
_URIs[_current] = uri;
if (++_current >= _size) _current = 0;
}
_references.put(uri, doc);
}
/**
*
*/
private synchronized void replaceDocument(String uri, CachedDocument doc) {
CachedDocument old = (CachedDocument)_references.get(uri);
if (doc == null)
insertDocument(uri, doc);
else
_references.put(uri, doc);
}
/**
* Returns a document either by finding it in the cache or
* downloading it and putting it in the cache.
*/
public DOM retrieveDocument(String baseURI, String href, Translet trs) {
CachedDocument doc;
String uri = href;
if (baseURI != null && baseURI.length() != 0) {
try {
uri = SystemIDResolver.getAbsoluteURI(uri, baseURI);
} catch (TransformerException te) {
// ignore
}
}
// Try to get the document from the cache first
if ((doc = lookupDocument(uri)) == null) {
doc = new CachedDocument(uri);
if (doc == null) return null; // better error handling needed!!!
doc.setLastModified(getLastModified(uri));
insertDocument(uri, doc);
}
// If the document is in the cache we must check if it is still valid
else {
long now = System.currentTimeMillis();
long chk = doc.getLastChecked();
doc.setLastChecked(now);
// Has the modification time for this file been checked lately?
if (now > (chk + REFRESH_INTERVAL)) {
doc.setLastChecked(now);
long last = getLastModified(uri);
// Reload document if it has been modified since last download
if (last > doc.getLastModified()) {
doc = new CachedDocument(uri);
if (doc == null) return null;
doc.setLastModified(getLastModified(uri));
replaceDocument(uri, doc);
}
}
}
// Get the references to the actual DOM and DTD handler
final DOM dom = doc.getDocument();
// The dom reference may be null if the URL pointed to a
// non-existing document
if (dom == null) return null;
doc.incAccessCount(); // For statistics
final AbstractTranslet translet = (AbstractTranslet)trs;
// Give the translet an early opportunity to extract any
// information from the DOM object that it would like.
translet.prepassDocument(dom);
return(doc.getDocument());
}
/**
* Outputs the cache statistics
*/
public void getStatistics(PrintWriter out) {
out.println("<h2>DOM cache statistics</h2><center><table border=\"2\">"+
"<tr><td><b>Document URI</b></td>"+
"<td><center><b>Build time</b></center></td>"+
"<td><center><b>Access count</b></center></td>"+
"<td><center><b>Last accessed</b></center></td>"+
"<td><center><b>Last modified</b></center></td></tr>");
for (int i=0; i<_count; i++) {
CachedDocument doc = (CachedDocument)_references.get(_URIs[i]);
out.print("<tr><td><a href=\""+_URIs[i]+"\">"+
"<font size=-1>"+_URIs[i]+"</font></a></td>");
out.print("<td><center>"+doc.getLatency()+"ms</center></td>");
out.print("<td><center>"+doc.getAccessCount()+"</center></td>");
out.print("<td><center>"+(new Date(doc.getLastReferenced()))+
"</center></td>");
out.print("<td><center>"+(new Date(doc.getLastModified()))+
"</center></td>");
out.println("</tr>");
}
out.println("</table></center>");
}
}