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. * It serializes the inputstream to a file (identifier => filename) (putInCache()) * or returns the inputstream based on the identifier (getFromCache()) * *

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 * */ 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 The current maximum cache counter */ 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; } }