From: <th...@us...> - 2004-01-06 10:25:01
|
Update of /cvsroot/jaxme/JaxMe/src/runtime/de/ispsoft/jaxme/tamino In directory sc8-pr-cvs1:/tmp/cvs-serv20967/src/runtime/de/ispsoft/jaxme/tamino Added Files: InoResponseHandlerNoNs.java InoManagerNoNs.java Log Message: handling of writing and receiving documents without namespace and map them into namespace aware docs (tamino workaround) --- NEW FILE: InoResponseHandlerNoNs.java --- package de.ispsoft.jaxme.tamino; import java.util.List; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; import org.apache.log4j.Category; /** <p>This is a SAX content handler for an ino:response document. It * provides a special handling for mapping the ino:id attribute to a * configurable attribute of the element class. This can be useful if the * applicationwide needed id attribute has to be mapped to the TaminoDB generated * ino:id, e.g. inoid. The mapping attribute name is set through the method * setAttributeMappingId.</p> * * @author <a href="mailto:jo...@is...">Jochen Wiedmann</a> * @author <a href="mailto:hae...@gm...">Jochen Wiedmann</a> */ public class InoResponseHandlerNoNs implements ContentHandler { private static final Category cat = Category.getInstance(InoResponseHandler.class.getName()); /** <p>The namespace of an INO response document: * <samp>http://namespaces.softwareag.com/tamino/response2</samp>.</p> */ public static final String INO_RESPONSE2_URI = "http://namespaces.softwareag.com/tamino/response2"; /** <p>The namespace of the XQL section in an INO response * document: <samp>http://metalab.unc.edu/xql/</samp>.</p> */ public static final String XQL_URI = "http://metalab.unc.edu/xql/"; private boolean inInoMessage = false; private boolean inInoMessageText; private boolean inXqlResult; private int level; private String inoErrorCode; private StringBuffer inoErrorMessage; private Locator locator; private ContentHandler resultHandler; private String idAttribute = null; private String idAttributeNS = null; private String targetNamespace = null; private int i; /** Creates a new InoResponseHandler */ public InoResponseHandlerNoNs() {} /** * Configure an ino:id mapping attribute. The first param idAttribute * provides the attributes name used in the JMAnyElement instance as id. * The idAttributeNS provides the namespace to be used for idAttribute. * @param idAttribute * @param idAttributeNS */ public void setAttributeMappingId(String idAttribute, String idAttributeNS) { this.idAttribute = idAttribute; this.idAttributeNS = idAttributeNS; //cat.debug("idAttribute: " + idAttribute); //cat.debug("idAttributeNS: " + idAttributeNS); } public void setTargetNamespace(String namespace) { targetNamespace = namespace; } public void setDocumentLocator(Locator l) { locator = l; } public void startDocument() throws org.xml.sax.SAXException { inInoMessage = false; inInoMessageText = false; inXqlResult = false; level = 0; if (inoObjectIdList != null) { inoObjectIdList.clear(); } } public void endDocument() throws org.xml.sax.SAXException { } public void startElement(String namespaceUri, String localName, String qName, Attributes attr) throws SAXException { if (inXqlResult) { if (resultHandler != null) { if (level == 2) { resultHandler.startDocument(); } // if idAttribute is configured if(idAttribute != null) { // is there an ino:id attribute? i = attr.getIndex(INO_RESPONSE2_URI, "id"); if(i > -1) { // there is an ino:id, map it to the configured one AttributesImpl attrs = new AttributesImpl(); attrs.setAttributes(attr); attrs.addAttribute(idAttributeNS, idAttribute, idAttribute, "CDATA", attr.getValue(i)); attr = attrs; int ix = attr.getIndex(idAttributeNS, idAttribute); } } if(targetNamespace != null) { namespaceUri = targetNamespace; } resultHandler.startElement(namespaceUri, localName, qName, attr); } } else if (inInoMessage) { if (level == 2) { if (INO_RESPONSE2_URI.equals(namespaceUri) && "messagetext".equals(localName)) { String c = attr.getValue(INO_RESPONSE2_URI, "code"); if (c != null) { inoErrorCode = c; } inInoMessageText = true; } } } else if (level == 1) { if (XQL_URI.equals(namespaceUri) && "result".equals(localName)) { inXqlResult = true; } else if (INO_RESPONSE2_URI.equals(namespaceUri)) { if ("message".equals(localName)) { String retval = attr.getValue(INO_RESPONSE2_URI, "returnvalue"); if (retval == null || !retval.equals("0")) { inoErrorCode = retval; inoErrorMessage = new StringBuffer(); inInoMessage = true; } } else if (inoObjectIdList != null && "object".equals(localName)) { inoObjectIdList.add(attr.getValue(INO_RESPONSE2_URI, "id")); } } } ++level; } public void endElement(String namespaceUri, String localName, String qName) throws SAXException { level--; if (inXqlResult) { if (level == 1) { inXqlResult = false; } else { if (resultHandler != null) { resultHandler.endElement(namespaceUri, localName, qName); if (level == 2) { resultHandler.endDocument(); } } } } else if (inInoMessage) { if (level == 1) { if (inoErrorCode == null) { inoErrorCode = "INOUNKNOWN"; } throw new InoException(inoErrorCode, inoErrorMessage.toString()); } else if (level == 2) { if (inInoMessageText) { inInoMessageText = false; } } } } public void startPrefixMapping(String namespaceUri, String prefix) throws SAXException { if (inXqlResult) { if (resultHandler != null) { resultHandler.startPrefixMapping(namespaceUri, prefix); } } } public void endPrefixMapping(String prefix) throws SAXException { if (inXqlResult) { if (resultHandler != null) { resultHandler.endPrefixMapping(prefix); } } } public void ignorableWhitespace(char[] ch, int start, int len) throws SAXException { if (inXqlResult) { if (resultHandler != null) { resultHandler.ignorableWhitespace(ch, start, len); } } } public void skippedEntity(String entity) throws SAXException { if (inXqlResult) { if (resultHandler != null) { resultHandler.skippedEntity(entity); } } } public void processingInstruction(String target, String data) throws SAXException { if (inXqlResult) { if (resultHandler != null) { resultHandler.processingInstruction(target, data); } } } public void characters(char[] ch, int start, int len) throws SAXException { if (inXqlResult) { if (resultHandler != null) { resultHandler.characters(ch, start, len); } } else if (inInoMessageText) { inoErrorMessage.append(ch, start, len); } } /** <p>Sets the result handler. The result handler is another SAX ContentHandler. * For any result document the InoResponseHandler finds, that is, for any * subelement of xql:result, a stream of SAX events is generated for the * result handler.</p> * <p>If the response document contains more than one result object, then the * result handler must be "restartable". In other words, it must be able to * process multiple startDocument ... endDocument startDocument ... * endDocument sequences.</p> * * @param handler The result handler to use or null to disable SAX events * @see #getResultHandler */ public void setResultHandler(ContentHandler handler) { resultHandler = handler; } /** <p>Returns a result handler, that was previously set with setResultHandler, * or null.</p> * <p>The result handler is another SAX ContentHandler. * For any result document the InoResponseHandler finds, that is, for any * subelement of xql:result, a stream of SAX events is generated for the * result handler.</p> * <p>If the response document contains more than one result object, then the * result handler must be "restartable". In other words, it must be able to * process multiple startDocument ... endDocument startDocument ... * endDocument sequences.</p> * * @return The result handler or null, if generating SAX events is disabled. */ public ContentHandler getResultHandler() { return resultHandler; } private List inoObjectIdList; /** <p>The Tamino response document contains object ID's of inserted * or updated objects. If you use this method, then the ID's are * collected in the given List. A null value disables ID * collection. The list will be cleared within <code>startDocument</code>, * so it's safe to reuse the list over multiple uses of the handler.</p> * <p>More precise, the list will contain all occurences of * ino:response/ino:object/@ino:id.</p> * * @param pList A list where ID's are being collected or null to disable * ID collection. * @see #getInoObjectIdList */ public void setInoObjectIdList(List pList) { inoObjectIdList = pList; } /** <p>Returns the current list for collection of generated ino:id's.</p> * * @see #setInoObjectIdList */ public List getInoObjectIdList() { return inoObjectIdList; } } --- NEW FILE: InoManagerNoNs.java --- package de.ispsoft.jaxme.tamino; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.URLEncoder; import java.util.ArrayList; import java.util.List; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.apache.log4j.Category; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.w3c.dom.Element; import org.w3c.dom.Node; import de.ispsoft.jaxme.JMAnyElement; import de.ispsoft.jaxme.JMContentHandler; import de.ispsoft.jaxme.JMManagerImpl; import de.ispsoft.jaxme.JMManagerFactoryImpl; import de.ispsoft.jaxme.Observer; /** <p>An implementation of a JMManager for a Tamino database. This * InoManager handles the tamino db access without namespace support. * The targetnamespace of outgoing xml documents is removed (XMLSerializerNoNS) * before writing it out. For incomming xml documents from tamino db the * targetnamespace is set through InoResponseHandlerNoNS. * A configuration example is provided below: * <JMManagerConfiguration xmlns="http://jaxme.ispsoft.de/namespaces/JMManagerConfiguration"> * <Defaults> * <InoManager> * <DbURL>http://localhost/tamino/DB/col</DbURL> * <ManagerClass>de.ispsoft.jaxme.tamino.InoManagerNoNS</ManagerClass> * <Id-Attribute>inoid</Id-Attribute> * <Id-Attribute-NS></Id-Attribute-NS> * <TargetNamespace-IN>http://my.namespace.com/data</TargetNamespace-IN> * </InoManager> * This example configures the tamino db access to use the InoManagerNoNS class * and map the ino:id attribute to inoid without an attribute namespace. The * targetnamespace for incomming xml documents from database is set to * http://my.namespace.com/data. *</p> * @see de.ispsoft.jaxme.XMLSerializerNoNS * @see de.ispsoft.jaxme.InoResponseHandlerNoNS * @author <a href="mailto:jo...@is...">Jochen Wiedmann</a> * @author <a href="mailto:hae...@gm...">Thomas Haenel</a> */ public class InoManagerNoNs extends JMManagerImpl { private static final Category cat = Category.getInstance(InoManagerNoNs.class.getName()); private URL dbURL; private String user; private String password; private boolean useGet; private SAXParserFactory spf; private String idAttribute = null; private String idAttributeNS = null; private String targetNamespaceIn = null; /** <p>Name of our private configuration node (<samp>InoManager</samp> in * namespace <code>JMManagerFactoryImpl.NAMESPACE_URI</code>)</p> */ public static final String CONFIGURATION_NODE_NAME = "InoManager"; /** <p>Name of the database URL node, including the collection name.</p> */ public static final String DBURL_NODE_NAME = "DbURL"; /** <p>Name of the User node</p> */ public static final String USER_NODE_NAME = "User"; /** <p>Name of the Password node</p> */ public static final String PASSWORD_NODE_NAME = "Password"; /** <p>Name of the attribute Id mapping. By default * no mapping is used.</p> */ public static final String ID_ATTRIBUTE = "Id-Attribute"; /** <p>The namespace for the mapped attribute. By default * no mapping is used.</p> */ public static final String ID_ATTRIBUTE_NS = "Id-Attribute-NS"; /** <p>The namespace to use for incomming documents from tamino db. * By default no namespace is used.</p> */ public static final String TARGETNAMESPACE_IN = "TargetNamespace-IN"; /** <p>Returns the element ID. The default implementation * returns * <code>getAttribute(InoResponseHandler.INO_RESPONSE2_URI, "id")</code>. * If you use another element as the ID, change this.</p> */ protected String getElementId(JMAnyElement pElement) throws SAXException { if(idAttribute != null) { String id = pElement.getAttribute(idAttributeNS, idAttribute, null); pElement.removeAttribute(idAttributeNS, idAttribute); return id; } else return pElement.getAttribute(InoResponseHandlerNoNs.INO_RESPONSE2_URI, "id", null); } /** <p>After an insert, sets the elements ID. The default * implementation performs * <code>setAttribute(InoResponseHandler.INO_RESPONSE2_URI, "id", pId)</code>. * If you use another element as the ID, change this. You probably need * to read the inserted document by using the supplied ino:id.</p> */ protected void setElementId(JMAnyElement pElement, String pId) throws SAXException { pElement.setAttribute(InoResponseHandlerNoNs.INO_RESPONSE2_URI, "id", pId, null); if(idAttribute != null) { pElement.setAttribute(idAttributeNS, idAttribute, pId, null); } } /** <p>Returns a query suited for deleting the element.</p> */ protected String getDeleteQuery(JMAnyElement pElement) throws SAXException { String id = getElementId(pElement); if (id == null || id.length() == 0) { throw new SAXException("The element being deleted doesn't have an ID."); } return "_delete=" + URLEncoder.encode(pElement.getLocalName() + "[@ino:id=" + id + "]"); } /** <p>Returns a query suited for updating the element.</p> */ protected String getUpdateQuery(JMAnyElement pElement) throws SAXException { String id = getElementId(pElement); if (id == null || id.length() == 0) { throw new SAXException("The element being updated doesn't have an ID."); } return "_process=" + URLEncoder.encode(pElement.toXMLNoNS()); } /** <p>Returns a query suited for inserting the element.</p> */ protected String getInsertQuery(JMAnyElement pElement) throws SAXException { String id = getElementId(pElement); if (id != null && id.length() > 0) { throw new SAXException("The element being inserted already has an ID."); } return "_process=" + URLEncoder.encode(pElement.toXMLNoNS()); } /** <p>Parses a configuration element. This method is called twice: * Once for the default configuration and once for the specific * configuration.</p> */ public void parseConfiguration(Element pElement) { super.parseConfiguration(pElement); for (Node node = pElement.getFirstChild(); node != null; node = node.getNextSibling()) { if (node.getNodeType() == Node.ELEMENT_NODE && JMManagerFactoryImpl.NAMESPACE_URI.equals(node.getNamespaceURI())) { if (CONFIGURATION_NODE_NAME.equals(node.getLocalName())) { for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) { if (child.getNodeType() == Node.ELEMENT_NODE && JMManagerFactoryImpl.NAMESPACE_URI.equals(child.getNamespaceURI())) { if (DBURL_NODE_NAME.equals(child.getLocalName())) { String url = JMManagerFactoryImpl.getNodeText(child); try { dbURL = new java.net.URL(url); } catch (MalformedURLException e) { throw new IllegalArgumentException("Invalid database URL: " + url); } } else if (USER_NODE_NAME.equals(child.getLocalName())) { user = JMManagerFactoryImpl.getNodeText(child); } else if (PASSWORD_NODE_NAME.equals(child.getLocalName())) { password = JMManagerFactoryImpl.getNodeText(child); } else if (ID_ATTRIBUTE.equals(child.getLocalName())) { idAttribute = JMManagerFactoryImpl.getNodeText(child); } else if (ID_ATTRIBUTE_NS.equals(child.getLocalName())) { idAttributeNS = JMManagerFactoryImpl.getNodeText(child); } else if (TARGETNAMESPACE_IN.equals(child.getLocalName())) { targetNamespaceIn = JMManagerFactoryImpl.getNodeText(child); } } } if(idAttribute != null && idAttributeNS == null) idAttributeNS = ""; } } } } /** Creates a new instance of InoManager */ public InoManagerNoNs() { spf = SAXParserFactory.newInstance(); spf.setNamespaceAware(true); spf.setValidating(false); } /** <p>Deletes the given document from the database.</p> */ public void delete(JMAnyElement pElement) throws SAXException { String query = getDeleteQuery(pElement); performQuery(query, (List) null); } /** <p>Inserts the given document into the database.</p> */ public void insert(JMAnyElement pElement) throws SAXException { String query = getInsertQuery(pElement); List idList = new ArrayList(); InoResponseHandlerNoNs irh = performQuery(query, idList); if (idList.size() == 0) { throw new SAXException("Query did not return an ino:id"); } setElementId(pElement, (String) idList.get(0)); } /** <p>Updates the given document in the database.</p> */ public void update(JMAnyElement pElement) throws SAXException { String query = getUpdateQuery(pElement); performQuery(query, (List) null); } /** <p>Defines the given schema in the database.</p> */ public void define(String pSchema) throws SAXException { String query = "_define=" + URLEncoder.encode(pSchema); performQuery(query, (java.util.List) null); } /** <p>Performs a single database query.</p> */ protected HttpURLConnection getResponse(String pQuery) throws SAXException { URL connectionURL = dbURL; try { if (useGet) { String dburl = connectionURL.toString(); String url; if (dburl.indexOf('?') > 0) { url = dburl + "&" + pQuery; } else { url = dburl + "?" + pQuery; } try { connectionURL = new URL(url); HttpURLConnection conn = (HttpURLConnection) connectionURL.openConnection(); conn.setDoOutput(false); conn.setDoInput(true); return conn; } catch (MalformedURLException e) { throw new SAXException("Malformed database URL: " + url); } } else { HttpURLConnection conn = (HttpURLConnection) connectionURL.openConnection(); conn.setDoOutput(true); conn.setDoInput(true); OutputStream ostream = conn.getOutputStream(); Writer w = new OutputStreamWriter(ostream); w.write(pQuery); w.close(); return conn; } } catch (IOException e) { throw new SAXException("I/O Error: " + e.getMessage(), e); } } /** <p>Performs a single database query.</p> */ protected InoResponseHandlerNoNs performQuery(String pQuery, java.util.List pList) throws SAXException { InoResponseHandlerNoNs irh = new InoResponseHandlerNoNs(); if (pList != null) { irh.setInoObjectIdList(pList); } performQuery(pQuery, irh); return irh; } /** <p>Parses a single INO response document.</p> */ protected void performQuery(String pQuery, InoResponseHandlerNoNs pHandler) throws SAXException { if(idAttribute != null) pHandler.setAttributeMappingId(idAttribute, idAttributeNS); if(targetNamespaceIn != null) pHandler.setTargetNamespace(targetNamespaceIn); HttpURLConnection conn = getResponse(pQuery); try { InputSource isource = new InputSource(conn.getInputStream()); isource.setEncoding(conn.getContentEncoding()); XMLReader xr; SAXParser sp = spf.newSAXParser(); xr = sp.getXMLReader(); xr.setContentHandler(pHandler); xr.parse(isource); } catch (ParserConfigurationException e) { throw new SAXException("ParserConfigurationException: " + e.getMessage(), e); } catch (IOException e) { throw new SAXException("I/O Exception: " + e.getMessage(), e); } } /** <p>Reads documents matching the given query. For any document * matching, the Observer's notify method is executed with the * matching document as an argument.</p> * <p>The query may contain placeholders. If it does, you have * to supply an object array with two elements per placeholder: * An Integer with a java.sql.Types type and the actual placeholder * value. Example: * <pre> * manager.select("Name = ? and Id = ?", * new Object[]{JMManager.VARCHAR, * "Someone", * JMManager.INTEGER, * 4}, 0, 0); * </pre></p> * * @param pObserver This Observer is notified for any matching document. * The document is added as an argument. * @param pQuery The query to perform. May contain placeholders. * @param pPlaceHolderArgs An array of objects or null, if the * query doesn't contain any placeholders. * @param pStart Ignore the given number of result documents at the * beginning. A value of zero will return all documents. * @param pMax Return at most the given number of documents. A value * of zero will return all documents. */ public void select(Observer pObserver, String pQuery, Object[] pPlaceHolderArgs, int pStart, int pMax) throws SAXException { if (pPlaceHolderArgs != null && pPlaceHolderArgs.length > 0) { throw new SAXException("Placeholders are not yet supported"); } String q; if (pStart != 0 || pMax != 0) { q = "_xql(" + (pStart+1) + "," + pMax + ")="; } else { q = "_xql="; } q += URLEncoder.encode(pQuery); InoResponseHandlerNoNs irh = new InoResponseHandlerNoNs(); JMContentHandler ch = getJMContentHandler(); ch.setObserver(pObserver); irh.setResultHandler(ch); performQuery(q, irh); } /** * @return */ public URL getDbURL() { return dbURL; } /** * @param url */ public void setDbURL(URL url) { dbURL = url; } } |