# HG changeset patch # User Sascha L. Teichmann # Date 1304089844 0 # Node ID fc3cf0ef777e819e23d9630ef1bab05d27e71214 # Parent dfbb3d50b0bd0f9f104badd9376432f39e839bf0 Added meta data service. flys-artifacts/trunk@1781 c6561f87-3c4e-4783-a992-168aeb5c3f6f diff -r dfbb3d50b0bd -r fc3cf0ef777e flys-artifacts/ChangeLog --- a/flys-artifacts/ChangeLog Fri Apr 29 14:39:42 2011 +0000 +++ b/flys-artifacts/ChangeLog Fri Apr 29 15:10:44 2011 +0000 @@ -1,3 +1,27 @@ +2011-04-29 Sascha L. Teichmann + + * src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java: + Forgot to add. + + * src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java: + New. Given a database connection and a XML template it generates + an output with meta data about the database. + + * src/main/java/de/intevation/flys/artifacts/services/meta/CompiledStatement.java: + New. Holds prepared statements optimized to be run in the stack of + contextes. + + * src/main/java/de/intevation/flys/artifacts/services/meta/StackFrames.java: + New. Model to hold a hierarchical scope of variables. + + * src/main/java/de/intevation/flys/artifacts/services/meta/ResultData.java: + New. Stores data set fetched from a sql select to be iterated in + a context. + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java, + src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Removed superfluous imports. + 2011-04-29 Sascha L. Teichmann * doc/conf/conf.xml: Added meta data service. diff -r dfbb3d50b0bd -r fc3cf0ef777e flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java --- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java Fri Apr 29 14:39:42 2011 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java Fri Apr 29 15:10:44 2011 +0000 @@ -13,7 +13,6 @@ import java.util.Comparator; import java.util.List; import java.util.Collections; -import java.util.Iterator; import org.apache.log4j.Logger; diff -r dfbb3d50b0bd -r fc3cf0ef777e flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java Fri Apr 29 15:10:44 2011 +0000 @@ -0,0 +1,105 @@ +package de.intevation.flys.artifacts.services; + +import java.io.InputStream; +import java.io.IOException; + +import java.sql.Connection; +import java.sql.SQLException; + +import org.w3c.dom.Document; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.CallMeta; +import de.intevation.artifacts.ServiceFactory; + +import de.intevation.artifactdatabase.DefaultService; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.flys.artifacts.services.meta.Builder; + +import de.intevation.flys.backend.SessionHolder; + +import org.hibernate.Session; + +import org.hibernate.jdbc.Work; + +public class MetaDataService +extends DefaultService +{ + private static Logger log = Logger.getLogger(MetaDataService.class); + + public static final String META_DATA_TEMPLATE = "/metadata/template.xml"; + + protected Builder builder; + + public MetaDataService() { + } + + @Override + public Document process( + Document data, + Object globalContext, + CallMeta callMeta + ) { + log.debug("MetaDataService.process"); + + final Document result = XMLUtils.newDocument(); + + if (builder == null) { + log.error("MetaDataService is not setup properly."); + return result; + } + + Session session = SessionHolder.acquire(); + try { + session.doWork(new Work() { + @Override + public void execute(Connection connection) + throws SQLException + { + log.debug("MetaDataService.execute"); + builder.build(connection, result); + } + }); + } + finally { + session.close(); + SessionHolder.release(); + } + + return result; + } + + @Override + public void setup(ServiceFactory factory, Object globalContext) { + log.debug("MetaDataService.setup"); + + InputStream in = getClass().getResourceAsStream(META_DATA_TEMPLATE); + + if (in == null) { + log.error("cannot get template resource"); + return; + } + + try { + Document template = XMLUtils.parseDocument(in); + if (template == null) { + log.error("cannot parse meta data template"); + } + else { + builder = new Builder(template); + } + } + finally { + try { + in.close(); + } + catch (IOException ioe) { + log.error(ioe); + } + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : diff -r dfbb3d50b0bd -r fc3cf0ef777e flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java Fri Apr 29 15:10:44 2011 +0000 @@ -0,0 +1,260 @@ +package de.intevation.flys.artifacts.services.meta; + +import java.util.regex.Matcher; + +import java.util.ArrayList; +import java.util.List; +import java.util.HashMap; +import java.util.Map; + +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; +import org.w3c.dom.Node; +import org.w3c.dom.Element; + +import java.sql.SQLException; +import java.sql.Connection; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import org.apache.log4j.Logger; + +public class Builder +{ + private static Logger log = Logger.getLogger(Builder.class); + + public static final String DC_NAMESPACE_URI = + "http://www.intevation.org/2011/Datacage"; + + protected Document template; + + public class BuildHelper + { + protected Document output; + protected StackFrames frames; + protected Connection connection; + protected Map statements; + + public BuildHelper() { + frames = new StackFrames(); + statements = new HashMap(); + } + + public BuildHelper(Document output, Connection connection) { + this(); + this.output = output; + this.connection = connection; + } + + public void build(List elements) throws SQLException { + try { + for (Node current: elements) { + build(output, current); + } + } + finally { + for (CompiledStatement cs: statements.values()) { + cs.close(); + } + statements.clear(); + } + } + + protected void context(Node parent, Element current) + throws SQLException + { + NodeList elements = current.getElementsByTagNameNS( + DC_NAMESPACE_URI, "elements"); + + if (elements.getLength() < 1) { + log.warn("no elements found -> ignore"); + return; + } + + NodeList subs = elements.item(0).getChildNodes(); + int S = subs.getLength(); + + if (S < 1) { + log.warn("elements is empty -> ignore"); + return; + } + + NodeList stmntNode = current.getElementsByTagNameNS( + DC_NAMESPACE_URI, "statement"); + + if (stmntNode.getLength() < 1) { + log.warn("dc:context: too less statements"); + return; + } + + String stmntText = stmntNode.item(0).getTextContent(); + + if (stmntText == null) { + log.warn("dc:context: no sql statement found"); + } + + CompiledStatement cs = statements.get(stmntText); + + if (cs == null) { + cs = new CompiledStatement(stmntText); + statements.put(stmntText, cs); + } + + ResultData rd = cs.execute(connection, frames); + + String [] columns = rd.getColumnLabels(); + + + for (Object [] row: rd.getRows()) { + frames.enter(); + try { + frames.put(columns, row); + for (int i = 0; i < S; ++i) { + build(parent, subs.item(i)); + } + } + finally { + frames.leave(); + } + } + } + + protected void element(Node parent, Element current) + throws SQLException + { + String attr = expand(current.getAttribute("name")); + + if (log.isDebugEnabled()) { + log.debug("dc:element -> '" + attr + "'"); + } + + if (attr.length() == 0) { + log.warn("no name attribute found"); + return; + } + + Element element = output.createElement(attr); + + NodeList children = current.getChildNodes(); + for (int i = 0, N = children.getLength(); i < N; ++i) { + build(element, children.item(i)); + } + + parent.appendChild(element); + } + + protected void text(Node parent, Element current) + throws SQLException + { + log.debug("dc:text"); + String value = expand(current.getTextContent()); + parent.appendChild(output.createTextNode(value)); + } + + + protected void attribute(Node parent, Element current) { + + if (parent.getNodeType() != Node.ELEMENT_NODE) { + log.warn("need element here"); + return; + } + + String name = expand(current.getAttribute("name")); + String value = expand(current.getAttribute("value")); + + Element element = (Element)parent; + + element.setAttribute(name, value); + } + + protected String expand(String s) { + Matcher m = CompiledStatement.VAR.matcher(s); + + StringBuffer sb = new StringBuffer(); + while (m.find()) { + String key = m.group(1); + Object value = frames.get(key); + m.appendReplacement(sb, value != null ? value.toString() : ""); + } + m.appendTail(sb); + return sb.toString(); + } + + protected void build(Node parent, Node current) + throws SQLException + { + String ns = current.getNamespaceURI(); + if (ns != null && ns.equals(DC_NAMESPACE_URI)) { + if (current.getNodeType() != Node.ELEMENT_NODE) { + log.warn("need elements here"); + } + else { + String localName = current.getLocalName(); + if ("context".equals(localName)) { + context(parent, (Element)current); + } + else if ("attribute".equals(localName)) { + attribute(parent, (Element)current); + } + else if ("element".equals(localName)) { + element(parent, (Element)current); + } + else if ("text".equals(localName)) { + text(parent, (Element)current); + } + else { + log.warn("unknown '" + localName + "' -> ignore"); + } + } + return; + } + + Node copy = output.importNode(current, false); + + NodeList children = current.getChildNodes(); + for (int i = 0, N = children.getLength(); i < N; ++i) { + build(copy, children.item(i)); + } + parent.appendChild(copy); + } + } // class BuildHelper + + + public Builder() { + } + + public Builder(Document template) { + this.template = template; + } + + public Document build(Connection connection) + throws SQLException + { + return build(connection, XMLUtils.newDocument()); + } + + public Document build(Connection connection, Document output) + throws SQLException + { + NodeList roots = template.getElementsByTagNameNS( + DC_NAMESPACE_URI, "template"); + + BuildHelper helper = new BuildHelper(output, connection); + + List elements = new ArrayList(); + + for (int i = 0, N = roots.getLength(); i < N; ++i) { + NodeList rootChildren = roots.item(i).getChildNodes(); + for (int j = 0, M = rootChildren.getLength(); j < M; ++j) { + Node child = rootChildren.item(j); + if (child.getNodeType() == Node.ELEMENT_NODE) { + elements.add(child); + } + } + } + helper.build(elements); + + return output; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : diff -r dfbb3d50b0bd -r fc3cf0ef777e flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/CompiledStatement.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/CompiledStatement.java Fri Apr 29 15:10:44 2011 +0000 @@ -0,0 +1,100 @@ +package de.intevation.flys.artifacts.services.meta; + +import java.util.regex.Pattern; +import java.util.regex.Matcher; + +import java.util.List; +import java.util.Map; +import java.util.HashMap; +import java.util.ArrayList; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Connection; +import java.sql.ResultSet; + +public class CompiledStatement +{ + public static final Pattern VAR = Pattern.compile("\\$\\{([a-zA-Z0-9_]+)\\}"); + + protected String original; + protected String statement; + + protected Map> positions; + + protected PreparedStatement preparedStatement; + + public CompiledStatement() { + } + + public CompiledStatement(String original) { + this.original = original; + positions = new HashMap>(); + compile(); + } + + protected void compile() { + + StringBuffer sb = new StringBuffer(); + + Matcher m = VAR.matcher(original); + + int index = 1; + + while (m.find()) { + String key = m.group(1); + List indices = positions.get(key); + if (indices == null) { + indices = new ArrayList(); + positions.put(key, indices); + } + indices.add(index); + m.appendReplacement(sb, "?"); + ++index; + } + + m.appendTail(sb); + + statement = sb.toString(); + } + + public String getStatement() { + return statement; + } + + public ResultData execute(Connection connection, StackFrames frames) + throws SQLException + { + if (preparedStatement == null) { + preparedStatement = connection.prepareStatement(statement); + } + + for (Map.Entry> entry: positions.entrySet()) { + Object value = frames.get(entry.getKey()); + for (Integer index: entry.getValue()) { + preparedStatement.setObject(index, value); + } + } + + ResultSet result = preparedStatement.executeQuery(); + try { + return new ResultData(preparedStatement.getMetaData()) + .addAll(result); + } + finally { + result.close(); + } + } + + public void close() { + if (preparedStatement != null) { + try { + preparedStatement.close(); + } + catch (SQLException sqle) { + } + preparedStatement = null; + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : diff -r dfbb3d50b0bd -r fc3cf0ef777e flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/ResultData.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/ResultData.java Fri Apr 29 15:10:44 2011 +0000 @@ -0,0 +1,57 @@ +package de.intevation.flys.artifacts.services.meta; + +import java.sql.ResultSetMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; + +import java.util.List; +import java.util.ArrayList; + +public class ResultData +{ + protected String [] columns; + + protected List rows; + + public ResultData() { + rows = new ArrayList(); + } + + public ResultData(ResultSetMetaData meta) + throws SQLException + { + this(); + + int N = meta.getColumnCount(); + + columns = new String[N]; + + for (int i = 1; i <= N; ++i) { + columns[i-1] = meta.getColumnLabel(i); + } + } + + public String [] getColumnLabels() { + return columns; + } + + public ResultData addAll(ResultSet result) throws SQLException { + while (result.next()) { + add(result); + } + return this; + } + + public void add(ResultSet result) throws SQLException { + Object [] row = new Object[columns.length]; + for (int i = 0; i < columns.length; ++i) { + row[i] = result.getObject(i+1); + } + rows.add(row); + } + + public List getRows() { + return rows; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : diff -r dfbb3d50b0bd -r fc3cf0ef777e flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/StackFrames.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/StackFrames.java Fri Apr 29 15:10:44 2011 +0000 @@ -0,0 +1,47 @@ +package de.intevation.flys.artifacts.services.meta; + +import java.util.Map; +import java.util.List; +import java.util.HashMap; +import java.util.ArrayList; + +public class StackFrames +{ + protected List> frames; + + public StackFrames() { + frames = new ArrayList>(); + } + + public void enter() { + frames.add(new HashMap()); + } + + public void leave() { + frames.remove(frames.size()-1); + } + + public void put(String [] keys, Object [] values) { + Map top = frames.get(frames.size()-1); + for (int i = 0; i < keys.length; ++i) { + top.put(keys[i], values[i]); + } + } + + public Object get(String key) { + return get(key, null); + } + + public Object get(String key, Object def) { + + for (int i = frames.size()-1; i >= 0; --i) { + Map frame = frames.get(i); + if (frame.containsKey(key)) { + return frame.get(key); + } + } + + return def; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : diff -r dfbb3d50b0bd -r fc3cf0ef777e flys-artifacts/src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java --- a/flys-artifacts/src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java Fri Apr 29 14:39:42 2011 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java Fri Apr 29 15:10:44 2011 +0000 @@ -4,7 +4,6 @@ import java.io.OutputStream; import java.util.ArrayList; import java.util.Date; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Vector;