📄 resultsettoxmlconverter.java
字号:
/*------------------------------------------------------------------------------Name: ResultSetToXmlConverter.javaProject: org.xmlBlasterProject: xmlBlaster.orgCopyright: xmlBlaster.org, see xmlBlaster-LICENSE file------------------------------------------------------------------------------*/package org.xmlBlaster.contrib.dbwatcher.convert;import java.util.logging.Logger;import java.io.ByteArrayOutputStream;import java.io.OutputStream;import java.util.Map;import java.util.Iterator;import java.util.Properties;import java.sql.Connection;import java.sql.ResultSet;import java.sql.ResultSetMetaData;import org.xmlBlaster.contrib.I_Info;import org.xmlBlaster.contrib.PropertiesInfo;import org.xmlBlaster.contrib.dbwatcher.ChangeEvent;/** * Creates a standardized XML dump from the given ResultSets. * <p /> * Configurations are: * <ul> * <li><tt>converter.rootName</tt> * The root tag name, defaults to <tt>sql</tt> * </li> * <li><tt>converter.addMeta</tt> * Suppress meta information, the CREATE statement however will * always transport the meta tags * </li> * <li><tt>converter.postStatement</tt> * A statement to be executed after a message has been published * </li> * <li><tt>charSet</tt> The encoding, defaults to <tt>UTF-8</tt></li> * <li><tt>transformer.class</tt> * If not empty or null the specified plugin implementing * {@link org.xmlBlaster.contrib.dbwatcher.convert.I_AttributeTransformer} is loaded. * This plugin is called once for each xml dump and adds <tt><attr></tt> tags as returned by the plugin * </li> * </ul> * <p> * Here is an example XML dump, note that all meta data settings (like isNullable) * are as described in JDBC (see ResultSetMetaData.java): * <pre><?xml version='1.0' encoding='UTF-8' ?><sql> <desc> <command>INSERT</command> <ident>AFTN_CIRCUIT_STATE</ident> <colname type='DATE' nullable='0'>DATUM</colname> <colname type='NUMBER' precision='11' signed='false'>CPU</colname> <colname type='NUMBER' precision='10' scale='3'>OLG</colname> <colname type='VARCHAR2' precision='8' nullable='0'>FS_ST</colname> </desc> <row num='0'> <col name='DATUM'>2005-01-05 15:41:36.0</col> <col name='CPU'>238089</col> <col name='OLG'>-12.333</col> <col name='FS_ST'>GW</col> <attr name='SUBNET_ID'>TCP</attr> <attr name='CIRCUIT_STATE'>OPERATIVE</attr> </row> <row num='1'> <col name='DATUM'>2005-01-05 15:41:36.0</col> <col name='CPU'>238092</col> <col name='OLG'>1.513</col> <col name='FS_ST'>GW</col> <attr name='SUBNET_ID'>TCP</attr> <attr name='CIRCUIT_STATE'>OPERATIVE</attr> </row></sql> * </pre> * <p> * The additional <attr> tags can be created by configuring an * {@link I_AttributeTransformer} plugin. * </p> * <p> * This class is not thread save, * use separate instances if used by multiple threads. * </p> * @see org.xmlBlaster.contrib.dbwatcher.convert.I_DataConverter * @author Marcel Ruff */public class ResultSetToXmlConverter implements I_DataConverter{ private static Logger log = Logger.getLogger(ResultSetToXmlConverter.class.getName()); protected I_AttributeTransformer transformer; protected String rootTag; protected OutputStream out; protected String command; protected String ident; protected int rowCounter; protected boolean commandIsAdded; protected boolean doneCalled; protected boolean addMeta; protected String postStatement; protected String charSet; private int maxRows; /** * Default constructor, you need to call <tt>init(info)</tt> thereafter. */ public ResultSetToXmlConverter() { // void } /** * Create this plugin. * @param info Possible configuration parameters you find in the class description * @throws Exception If transformer instantiation fails */ public ResultSetToXmlConverter(I_Info info) throws Exception { init(info); } /** * @see org.xmlBlaster.contrib.dbwatcher.convert.I_DataConverter#init(I_Info) */ public synchronized void init(I_Info info) throws Exception { this.rootTag = info.get("converter.rootName", "sql"); this.addMeta = info.getBoolean("converter.addMeta", true); this.postStatement = info.get("converter.postStatement", (String)null); if (this.postStatement != null) { this.postStatement = this.postStatement.trim(); if (this.postStatement.length() < 1) this.postStatement = null; } this.charSet = info.get("charSet", "UTF-8"); this.maxRows = info.getInt("converter.maxRows", 0); ClassLoader cl = this.getClass().getClassLoader(); String transformerClassName = info.get("transformer.class", ""); if (transformerClassName != null && transformerClassName.length() > 0) { this.transformer = (I_AttributeTransformer)cl.loadClass(transformerClassName).newInstance(); this.transformer.init(info); log.info("Loaded transformer pluing '" + transformerClassName + "'"); } } /** * This should be called before the first #addInfo(ResultSet) call. * @param event can be null since it is not used in this implementation. * @see org.xmlBlaster.contrib.dbwatcher.convert.I_DataConverter#setOutputStream(OutputStream, String, String) */ public void setOutputStream(OutputStream out, String command, String ident, ChangeEvent event) throws Exception { if (this.out != null) { try { this.out.close(); } catch (java.io.IOException e) { /* Ignore */ } } this.out = out; this.command = command; this.ident = ident; this.rowCounter = 0; this.doneCalled = false; this.commandIsAdded = false; StringBuffer buf = new StringBuffer("<?xml version='1.0' encoding='").append(this.charSet).append("' ?>"); buf.append("\n<").append(rootTag).append(">"); this.out.write(buf.toString().getBytes(this.charSet)); } /** * Add a map with attributes to the XML string. * @see org.xmlBlaster.contrib.dbwatcher.convert.I_DataConverter#addInfo(Map) */ public void addInfo(Map attributeMap) throws Exception { if (attributeMap == null) throw new IllegalArgumentException("ResultSetToXmlConverter: Given attribute map is null"); if (this.out == null) throw new IllegalArgumentException("ResultSetToXmlConverter: Please call setOutputStream() first"); StringBuffer buf = new StringBuffer(128*(attributeMap.size()+1)); Iterator it = attributeMap.keySet().iterator(); while (it.hasNext()) { String name = (String)it.next(); // Possible attributes are 'name', 'size', 'type', 'encoding', see org.xmlBlaster.util.EncodableData buf.append("\n <attr name='").append(name).append("'"); String value = (String)attributeMap.get(name); int need = protectionNeeded(value); if (need == 0) buf.append(">").append(value==null?"":value); else if (need == 1) buf.append("><![CDATA[").append(value).append("]]>"); else if (need == 2) buf.append(" encoding='").append(BASE64).append("'>").append(Base64.encode(value.getBytes())); buf.append("</attr>"); } if (buf.length() > 0) this.out.write(buf.toString().getBytes(this.charSet)); } /** * Add another result set to the XML string * @see org.xmlBlaster.contrib.dbwatcher.convert.I_DataConverter#addInfo(ResultSet, int) */ public void addInfo(Connection conn, ResultSet rs, int what) throws Exception { if (rs == null) throw new IllegalArgumentException("ResultSetToXmlConverter: Given ResultSet is null"); if (this.out == null) throw new IllegalArgumentException("ResultSetToXmlConverter: Please call setOutputStream() first"); if (this.maxRows > 0 && this.rowCounter > this.maxRows) return; ResultSetMetaData meta = rs.getMetaData(); int numberOfColumns = meta.getColumnCount(); StringBuffer buf = new StringBuffer(4096); if (this.rowCounter == 0L) { // Create the header meta information buf.append("\n <desc>"); //buf.append("\n <statement><![CDATA["); //buf.append(rs.getStatement().toString()); // is not available! //buf.append("]]></statement>"); if (this.command != null) { buf.append("\n <command>").append(this.command).append("</command>"); } if (this.ident != null) { buf.append("\n <ident>").append(this.ident).append("</ident>"); } // The CREATE command will allways have meta informations added if ((what == ALL && this.addMeta == true) || what == META_ONLY) { // rs.isFirst()) { for (int i=1; i<=numberOfColumns; i++) { buf.append("\n <colname"); String tn = meta.getTableName(i); if (tn != null && tn.length() > 0) buf.append(" table='").append(meta.getTableName(i)).append("'"); String schema = meta.getSchemaName(i); if (schema != null && schema.length() > 0) buf.append(" schema='").append(schema).append("'"); String catalog = meta.getCatalogName(i); if (catalog != null && catalog.length() > 0) buf.append(" catalog='").append(catalog).append("'"); buf.append(" type='").append(meta.getColumnTypeName(i)).append("'"); if (meta.getPrecision(i) > 0) buf.append(" precision='").append(meta.getPrecision(i)).append("'"); if (meta.getScale(i) > 0) buf.append(" scale='").append(meta.getScale(i)).append("'"); // always write this since it is not a boolean and it has no default ... buf.append(" nullable='").append(meta.isNullable(i)).append("'"); if (meta.isSigned(i)==false) buf.append(" signed='").append(meta.isSigned(i)).append("'"); if (meta.isReadOnly(i)==true) buf.append(" readOnly='").append(meta.isReadOnly(i)).append("'"); buf.append(">"); buf.append(meta.getColumnName(i)); buf.append("</colname>"); } } if (this.transformer != null) { Map attr = this.transformer.transform(rs, -1); if (attr != null) { this.out.write(buf.toString().getBytes(this.charSet)); buf.setLength(0); addInfo(attr); } } buf.append("\n </desc>"); this.commandIsAdded = true; } if (what == ALL || what == ROW_ONLY) { buf.append("\n <row num='").append(""+this.rowCounter).append("'>"); this.rowCounter++; for (int i=1; i<=numberOfColumns; i++) { // Possible attributes are 'name', 'size', 'type', 'encoding', see org.xmlBlaster.util.EncodableData buf.append("\n <col name='").append(meta.getColumnName(i)).append("'"); String value = rs.getString(i); int need = protectionNeeded(value); if (need == 0) buf.append(">").append(value==null?"":value); else if (need == 1) buf.append("><![CDATA[").append(value).append("]]>"); else if (need == 2) buf.append(" encoding='").append(BASE64).append("'>").append(Base64.encode(value.getBytes())); buf.append("</col>"); } if (this.transformer != null) { Map attr = this.transformer.transform(rs, this.rowCounter); if (attr != null) { this.out.write(buf.toString().getBytes(this.charSet)); buf.setLength(0); addInfo(attr); } } buf.append("\n </row>"); } this.out.write(buf.toString().getBytes(this.charSet)); /* isLast() is not always available! if (rs.isLast()) { done(); log.info("Created new XML dump"); } */ } /** * If value contains XML harmful characters it needs to be * wrapped by CDATA or encoded to Base64. * @param value The string to verify * @return 0 No protection necessary * 1 Protection with CDATA is needed * 2 Protection with Base64 is needed */ protected int protectionNeeded(String value) { if (value == null) return 0; if (value.indexOf("]]>") >= 0) return 2; for (int i=0; i<value.length(); i++) { int c = value.charAt(i); if (c == '<' || c == '&') return 1; } return 0; } /** * @see org.xmlBlaster.contrib.dbwatcher.convert.I_DataConverter#done */ public int done() throws Exception { //java.io.UnsupportedEncodingException, java.io.IOException { if (this.out == null) return 0; if (this.doneCalled) return this.rowCounter; this.doneCalled = true; StringBuffer buf = new StringBuffer(); if (!this.commandIsAdded) { buf.append("\n <desc>"); if (this.command != null) { buf.append("\n <command>").append(this.command).append("</command>"); } if (this.ident != null) { buf.append("\n <ident>").append(this.ident).append("</ident>"); } buf.append("\n </desc>"); } buf.append("\n</").append(this.rootTag).append(">"); this.out.write(buf.toString().getBytes(this.charSet)); this.out.flush(); return this.rowCounter; } /** * @see org.xmlBlaster.contrib.dbwatcher.convert.I_DataConverter#shutdown */ public synchronized void shutdown() throws Exception { if (this.transformer != null) { this.transformer.shutdown(); this.transformer = null; } } public String getPostStatement() { return this.postStatement; } public static byte[] getResultSetAsXmlLiteral(Connection conn, ResultSet rs, String command, String ident, long maxRows) throws Exception { Properties props = new Properties(); if (maxRows > 0L) { if (maxRows > Integer.MAX_VALUE) throw new Exception("Too many maxRows: '" + maxRows + "' but should be maximum '" + Integer.MAX_VALUE + "'"); props.put("converter.maxRows", "" + maxRows); } ResultSetToXmlConverter converter = new ResultSetToXmlConverter(new PropertiesInfo(props)); ByteArrayOutputStream baos = new ByteArrayOutputStream(); converter.setOutputStream(baos, command, ident, null); while (rs.next()) converter.addInfo(conn, rs, ResultSetToXmlConverter.ROW_ONLY); converter.done(); converter.shutdown(); return baos.toByteArray(); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -