package eu.clarin.cmdi.mdservice.action;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
/*import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
*/
import java.util.Date;
import java.util.HashMap;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import javax.xml.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.sax.*;
import javax.xml.transform.stream.*;import javax.xml.transform.dom.*;
import javax.xml.xpath.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import org.xml.sax.InputSource;
import org.apache.commons.lang.StringEscapeUtils;
import eu.clarin.cmdi.mdservice.action.Admin;
import eu.clarin.cmdi.mdservice.model.Query;
/**
* This is a rudimentary caching mechanism.
* serializes the inputstream to a file (identifier => filename)
* and returns the inpustream based on the identifier.
*
* It maintains the cache_index (persisted as xml-file in the cache-folder)
* mapping the request parameters to the indexed files.
*
* This is an internal object, direct user access to cache-data via interface is only possible via AdminAction
* @author master
*
*/
/* TODO: necessary to maintain own index
* for resolution of IDs to filenames
*/
public class Cache {
public static String PREFIX = "xc_";
public static String USE = "use";
public static String REFRESH = "refresh";
public static String SKIP= "skip";
private static Cache singleton;
private String cachepath;
private String cacheindex_path;
private static Integer cachecounter;
private static final Integer start_cache = 1;
private static Document cacheindex_doc;
public Cache () {
cachepath = Admin.getConfig().getProperty("cache.path");
cacheindex_path = cachepath + Admin.getConfig().getProperty("cacheindex.file");
cachecounter = initCachecounter();
}
public static Cache getCache() {
if (singleton == null) {
singleton = new Cache();
}
return singleton;
}
public String getCacheIndexPath() {
return getCache().cacheindex_path;
}
protected void finalize() throws Throwable {
try {
updateCachecounter();
} catch(Exception e) {
}
finally {
super.finalize();
//more code can be written here as per need of application
}
}
/**
* Here the external key for the data gets cache-internal counter-id assigned
* which is also returned back.
*
* The external key gets parsed and put as individual attributes in the -element:
*
*
*
*
*
* @param key_info a complex string carrying all relevant parameters
* @param instream data to be stored
* @return
*/
public String putInCache(String key_info, InputStream instream) {
Admin.notifyUser("CACHE.putInCache:"+key_info);
Integer c = getCounter();
String xc;
//update xml_structure
Element e = cacheindex_doc.createElement("f");
//Attr attr = cacheindex_doc.createAttribute("id");
//attr.setValue(c.toString());
//e.setNamedItem(attr);
e.setAttribute("id", c.toString());
String[] key_array = key_info.split("//-");
e.setAttribute("type", key_array[0]);
if (key_array.length > 1){
String query_str;
if (key_array[0].equals("recordset")){
query_str = transformQuery(key_array[1]);
} else {
query_str = key_array[1];
}
e.setAttribute("query", query_str);
}else{
e.setAttribute("query", "");
}
if (key_array.length > 2){
e.setAttribute("collection", key_array[2]);
}else{
e.setAttribute("collection", "");
}
if (key_array.length > 3){
e.setAttribute("squery", key_array[3]);
}else{
e.setAttribute("squery", "");
}
if (key_array.length > 4){
e.setAttribute("startItem", key_array[4]);
}else{
e.setAttribute("startItem", "");
}
if (key_array.length > 5){
e.setAttribute("maximumItems", key_array[5]);
}else{
e.setAttribute("maximumItems", "");
}
if (key_array.length > 6){
e.setAttribute("repository", key_array[6]);
}else{
e.setAttribute("repository", "");
}
if (key_array.length > 7){
e.setAttribute("maxDepth", key_array[7]);
}else{
e.setAttribute("maxDepth", "");
}
if (key_array.length > 8){
e.setAttribute("lang", key_array[8]);
}else{
e.setAttribute("lang", "");
}
if (key_array.length > 9){
e.setAttribute("duration", key_array[9]);
}else{
e.setAttribute("duration", "");
}
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
e.setAttribute("date", dateFormat.format(new Date()).toString());
cacheindex_doc.getFirstChild().appendChild(e);
// update XML-counter
//Admin.writeToFile(formPath(id + xc),instream);
updateCachecounter();
xc = PREFIX + key_array[0] + "_" + c.toString();
Admin.notifyUser("CACHE.putInCache.filename:" + xc + ".xml");
Admin.writeToFile(cachepath + xc + ".xml",instream);
return xc;
}
public InputStream getFromCache(String key_info) {
File f = new File (formPath(key_info));
//Admin.notifyUser("CACHE.getFromCache:"+key_info);
if (f.exists()) { // read from file to InputStream;
Admin.notifyUser("CACHE.getFromCache.fromfile:"+f.getName() + "[#" + key_info + "]");
InputStream instream;
try {
instream = new FileInputStream(f);
return instream;
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
} else { //not cached, my dear
Admin.notifyUser("CACHE.getFromCache: NOT CACHED" + "[#" + key_info + "]");
return null;
}
}
private String transformQuery (String str) {
String transformed;
Query query;
if (str == null) return "";
if (str.trim().length() == 0) return "";
// provisional hack, setting simple string to ""
query = new Query("", str,"recordset","");
if (query.isStatus(Query.PARSEERROR)) {
Admin.notifyUser("Cache.transformQuery.PARSEERROR:" + query.getMsg());
// pass this bad news to the client
//setUserMsg(query.getMsg());
transformed = "";
} else {
transformed = Query.getSimpleQueryString(str);
transformed = StringEscapeUtils.escapeXml(transformed);
//Admin.notifyUser("CACHE.transformQuery:"+transformed);
}
return transformed;
}
//TODO: sanitize-key
public String formPath (String key_info) {
String id="";
String path="";
String xpath_expr="";
String[] key_array = key_info.split("//-");
String query_str;
if(key_array.length > 1){
// Admin.notifyUser("formPATH- querystring:" + key_array[1]);
if (key_array[0].equals("recordset")){
query_str = this.transformQuery(key_array[1]);
// Admin.notifyUser("formPATH- querystring-transformed:" + query_str);
}
else {
query_str = key_array[1];
}
} else {
query_str = "";
}
xpath_expr = "//index/f[@type='" + key_array[0] + "'";
xpath_expr = xpath_expr + " and @query='"+query_str+"'";
if (key_array.length > 2){
xpath_expr = xpath_expr + "and @collection='"+key_array[2]+"'";
}else{
xpath_expr = xpath_expr + " and @collection=''";
}
if (key_array.length > 3){
xpath_expr = xpath_expr + "and @squery='"+key_array[3]+"'";
}else{
xpath_expr = xpath_expr + " and @squery=''";
}
if (key_array.length > 4){
xpath_expr = xpath_expr + "and @startItem='"+key_array[4]+"'";
}else{
xpath_expr = xpath_expr + " and @startItem=''";
}
if (key_array.length > 5){
xpath_expr = xpath_expr + "and @maximumItems='"+key_array[5]+"'";
}else{
xpath_expr = xpath_expr + " and @maximumItems=''";
}
if (key_array.length > 6){
xpath_expr = xpath_expr + "and @repository='"+key_array[6]+"'";
}else{
xpath_expr = xpath_expr + " and @repository=''";
}
if (key_array.length > 7){
xpath_expr = xpath_expr + "and @maxDepth='"+key_array[7]+"'";
}else{
xpath_expr = xpath_expr + " and @maxDepth=''";
}
if (key_array.length > 8){
xpath_expr = xpath_expr + "and @lang='"+key_array[8]+"']";
}else{
xpath_expr = xpath_expr + " and @lang='']";
}
//Admin.notifyUser("formPath:xpath:"+xpath_expr);
//creating an XPathFactory:
XPathFactory factory = XPathFactory.newInstance();
//using this factory to create an XPath object:
XPath xpath = factory.newXPath();
//XPath object created compiles the XPath expression:
XPathExpression expr;
try {
expr = xpath.compile(xpath_expr);
//expression is evaluated with respect to a certain context node which is doc.
Object result = expr.evaluate(cacheindex_doc, XPathConstants.NODESET);
NodeList list = (NodeList) result;
if (list.getLength() > 0) {
id = list.item(0).getAttributes().getNamedItem("id").getNodeValue();
path = cachepath + PREFIX + key_array[0] + "_"+id + ".xml";
} else if (list.getLength() > 1){
Admin.notifyUser("formPath ERROR:!!!:multiple paths:"+key_info);
}
} catch (XPathExpressionException e) {
// TODO Auto-generated catch block
Admin.notifyUser("ERROR Cache.formPath: "+xpath_expr);
}
return path;
}
public Integer getCounter () {
cachecounter = cachecounter +1 ;
return cachecounter;
}
/**
* read counter-integer from file in cache
* or start that file
* @return
*/
public Integer initCachecounter() {
Integer counter = start_cache;
boolean init=false;
String fname = cacheindex_path;
File f = new File (fname);
//Admin.notifyUser("initCacheCounter");
if (!f.exists()) {
// create new counter document
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder;
try {
docBuilder = docFactory.newDocumentBuilder();
cacheindex_doc = docBuilder.newDocument();
// append root tag
Element root = (Element) cacheindex_doc.createElement("index");
root.setAttribute("idcounter", "1");
cacheindex_doc.appendChild(root);
writeCachecounter(counter);
//Admin.notifyUser("new document");
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//} catch (IOException e2) {
// Admin.notifyUser("ERROR creating cache counter");
// e2.printStackTrace();
//}
}
else {
try {
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder;
try {
docBuilder = docFactory.newDocumentBuilder();
try {
cacheindex_doc = docBuilder.parse(fname);
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//read counter
counter = new Integer(cacheindex_doc.getFirstChild().getAttributes().getNamedItem("idcounter").getNodeValue());
init = true;
} catch (IOException ex){
//Admin.notifyUser("initCacheCounter:" + ex.toString());
ex.printStackTrace();
}
}
return counter;
/*
//use buffering, reading one line at a time
//FileReader always assumes default encoding is OK!
BufferedReader input = new BufferedReader(new FileReader(f));
try {
String line = null; //not declared within while loop
/*
* readLine is a bit quirky :
* it returns the content of a line MINUS the newline.
* it returns null only for the END of the stream.
* it returns an empty String if two newlines appear in a row.
*
try {
if (( line = input.readLine()) != null){
try {
counter = new Integer(line);
} catch (NumberFormatException e) {
// if not a number, write a number
init=true;
}
} else {
init=true;
}
} catch (IOException e) {
init=true;
}
if (init) {
writeCachecounter( start_cache);
}
} finally {
input.close();
}
*/
}
public void updateCachecounter () {
//Admin.notifyUser("CACHEcounter:" + cachecounter);
writeCachecounter( cachecounter);
}
public void writeCachecounter (Integer i) {
// first update
cacheindex_doc.getFirstChild().getAttributes().getNamedItem("idcounter").setNodeValue(i.toString());
//Admin.notifyUser("writeCacheCounter:" + i.toString());
// write xml
Transformer transformer;
try {
transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
//initialize StreamResult with File object to save to file
StreamResult result = new StreamResult(new StringWriter());
DOMSource source = new DOMSource(cacheindex_doc);
try {
transformer.transform(source, result);
} catch (TransformerException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
String xmlString = result.getWriter().toString();
//Admin.notifyUser("writeCacheCounter:" + xmlString);
//Admin.writeToFile("", result);
File f = new File (cacheindex_path);
FileWriter fw;
try {
fw = new FileWriter(f);
try {
fw.write(xmlString);
fw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (TransformerConfigurationException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
} catch (TransformerFactoryConfigurationError e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
/*
File f = new File (Admin.getConfig().getProperty("cache.path") + "counter.txt");
try {
Writer output = new BufferedWriter(new FileWriter(f));
//FileWriter always assumes default encoding is OK!
try {
output.write( i);
} finally {
output.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
*/
}
public InputStream clear(HashMap clear_params) throws ParserConfigurationException, TransformerConfigurationException, TransformerException, TransformerFactoryConfigurationError {
String xpath_expr = "";
Integer removed = 0;
InputStream is = null;
if (clear_params.size() > 0 ) {
if (clear_params.containsKey("repository")){
xpath_expr = xpath_expr + " and @repository='" + clear_params.get("repository") + "'";
}
if (clear_params.containsKey("type")){
xpath_expr = xpath_expr + " and @type='" + clear_params.get("type") + "'";
}
if (clear_params.containsKey("date")){
xpath_expr = xpath_expr + " and @date='" + clear_params.get("date") + "'";
}
if (clear_params.containsKey("datefrom")){
xpath_expr = xpath_expr + " and @date >= '" + clear_params.get("datefrom") + "'";
}
if (clear_params.containsKey("dateto")){
xpath_expr = xpath_expr + " and @date <= '" + clear_params.get("dateto") + "'";
}
if (xpath_expr.length()>0){
xpath_expr = xpath_expr.substring(5);
}
xpath_expr = "//index/f[" + xpath_expr + "]";
} else {
xpath_expr = "//index/f";
}
////////// remove index_lines + files
String id,type,path;
File f;
//creating an XPathFactory:
XPathFactory factory = XPathFactory.newInstance();
//using this factory to create an XPath object:
XPath xpath = factory.newXPath();
//XPath object created compiles the XPath expression:
XPathExpression expr;
try {
expr = xpath.compile(xpath_expr);
//expression is evaluated with respect to a certain context node which is doc.
Object result = expr.evaluate(cacheindex_doc, XPathConstants.NODESET);
NodeList list = (NodeList) result;
//create deleted_doc
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder;
docBuilder = docFactory.newDocumentBuilder();
Document deleted_doc = docBuilder.newDocument();
Element root = (Element) deleted_doc.createElement("index");
root.setAttribute("count_deleted", String.valueOf(list.getLength()) );
deleted_doc.appendChild(root);
// delete items
for(int i = 0;i< list.getLength();i++){
id = list.item(i).getAttributes().getNamedItem("id").getNodeValue();
type = list.item(i).getAttributes().getNamedItem("type").getNodeValue();
path = cachepath + PREFIX + type + "_"+id + ".xml";
f = new File (path);
f.delete();
cacheindex_doc.getFirstChild().removeChild(list.item(i));
Element e = deleted_doc.createElement("f");
e.setAttribute("filename", path);
e.setAttribute("id", id);
deleted_doc.getFirstChild().appendChild(e);
}
//create resultstream
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Source xmlSource = new DOMSource(deleted_doc);
Result outputTarget = new StreamResult(outputStream);
TransformerFactory.newInstance().newTransformer().transform(xmlSource, outputTarget);
is = new ByteArrayInputStream(outputStream.toByteArray());
removed = list.getLength();
} catch (XPathExpressionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//////// set the index value
NodeList nl = cacheindex_doc.getElementsByTagName("f");
if (nl.getLength() < 1){
cachecounter = 1;
}else {
cachecounter = Integer.parseInt(nl.item(nl.getLength()-1).getAttributes().getNamedItem("id").getNodeValue());
}
this.updateCachecounter();
return is;
}
}