001: /*****************************************************************************
002: * Source code information
003: * -----------------------
004: * Original author Ian Dickinson, HP Labs Bristol
005: * Author email ian.dickinson@hp.com
006: * Package Jena 2
007: * Web http://sourceforge.net/projects/jena/
008: * Created 11-Sep-2003
009: * Filename $RCSfile: DIGConnection.java,v $
010: * Revision $Revision: 1.16 $
011: * Release status $State: Exp $
012: *
013: * Last modified on $Date: 2008/01/02 12:07:11 $
014: * by $Author: andy_seaborne $
015: *
016: * (c) Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP
017: * [See end of file]
018: *****************************************************************************/package com.hp.hpl.jena.reasoner.dig;
019:
020: // Imports
021: ///////////////
022: import java.io.*;
023: import java.net.*;
024: import java.util.*;
025:
026: import javax.xml.parsers.*;
027:
028: import org.apache.commons.logging.Log;
029: import org.apache.commons.logging.LogFactory;
030: import org.apache.xml.serialize.*;
031: import org.w3c.dom.*;
032:
033: import com.hp.hpl.jena.util.FileUtils;
034:
035: /**
036: * <p>
037: * Encapsulates the connection to a DIG reasoner.
038: * </p>
039: *
040: * @author Ian Dickinson, HP Labs (<a href="mailto:Ian.Dickinson@hp.com">email</a>)
041: * @version Release @release@ ($Id: DIGConnection.java,v 1.16 2008/01/02 12:07:11 andy_seaborne Exp $)
042: */
043: public class DIGConnection {
044: // Constants
045: //////////////////////////////////
046:
047: /** Default URL for connecting to a local DIG reasoner on port 8081 */
048: public static final String DEFAULT_REASONER_URL = "http://localhost:8081";
049:
050: /** Namespace for XSI */
051: public static final String XSI = "http://www.w3.org/2001/XMLSchema-instance";
052:
053: // Static variables
054: //////////////////////////////////
055:
056: private static Log log = LogFactory.getLog(DIGConnection.class);
057:
058: // Instance variables
059: //////////////////////////////////
060:
061: /** The URL to connect to, initialised to the default URL */
062: protected String m_extReasonerURL = DEFAULT_REASONER_URL;
063:
064: /** URI of current KB */
065: private String m_kbURI;
066:
067: /** The XML document builder we are using */
068: protected DocumentBuilderFactory m_factory = DocumentBuilderFactory
069: .newInstance();
070:
071: /** List of most recent warnings */
072: private List m_warnings = new ArrayList();
073:
074: /** Flag to control whether we log incoming and outgoing messages */
075: protected boolean m_logCommunications = true;
076:
077: // Constructors
078: //////////////////////////////////
079:
080: // External signature methods
081: //////////////////////////////////
082:
083: /**
084: * <p>Send a verb to the attached DIG reasoner and answer the result. The verb is encoded as an XML
085: * document object.</p>
086: * @param digVerb A DIG verb (information request, ask or tell) as an XML document
087: * @return The resulting XML document formed from the response from the reasoner
088: * @exception DigReasonerException for any errors in XML encoding or HTTP transmission
089: */
090: public Document sendDigVerb(Document digVerb, DIGProfile profile) {
091: try {
092: // make sure we set the KB uri
093: Element verb = digVerb.getDocumentElement();
094: if (!verb.hasAttribute(DIGProfile.URI)) {
095: verb.setAttribute(DIGProfile.URI, m_kbURI);
096: }
097:
098: // first open the connection
099: URL url = new URL(m_extReasonerURL);
100: HttpURLConnection conn = (HttpURLConnection) url
101: .openConnection();
102:
103: // pre-serialise the content so we can set the Content-Length field correctly
104: StringWriter out = new StringWriter();
105: serialiseDocument(digVerb, out);
106:
107: conn.setDoOutput(true);
108: conn.setRequestMethod("POST");
109: conn.setRequestProperty("Content-Length", Integer
110: .toString(out.getBuffer().length()));
111:
112: // different varaints on the protocol make different choices here
113: conn.setRequestProperty("Content-Type", profile
114: .getContentType());
115:
116: // log
117: logMessage(true, digVerb);
118:
119: // send
120: conn.connect();
121: PrintWriter pw = FileUtils.asPrintWriterUTF8(conn
122: .getOutputStream());
123: pw.print(out.getBuffer());
124: pw.flush();
125: pw.close();
126:
127: // and receive
128: Document response = getDigResponse(conn);
129:
130: // log
131: logMessage(false, response);
132:
133: errorCheck(response, profile);
134: return response;
135: } catch (IOException e) {
136: throw new DIGWrappedException(e);
137: }
138: }
139:
140: /**
141: * <p>Serialise the given document to the given output writer.</p>
142: * @param doc An XML document to serialise
143: * @param out A writer that will consume the seralised form of the document
144: */
145: public void serialiseDocument(Document doc, Writer out) {
146: try {
147: // write the given document to the string buffer
148: XMLSerializer serializer = new XMLSerializer(out,
149: createXMLFormatter(doc));
150: serializer.asDOMSerializer();
151: serializer.serialize(doc);
152: } catch (IOException e) {
153: throw new DIGWrappedException(e);
154: }
155: }
156:
157: /**
158: * <p>Bind a DIG KB to this adapter, by requesting a KB URI through the newKB
159: * verb. If there is already a binding, do nothing unless rebind is true.
160: * @param rebind If true, any existing KB will be released before binding
161: * to a new KB
162: */
163: public void bindKB(boolean rebind, DIGProfile profile) {
164: // delete the old KB
165: if (rebind && m_kbURI != null) {
166: Document release = createDigVerb(DIGProfile.RELEASEKB,
167: profile);
168:
169: Document response = sendDigVerb(release, profile);
170: errorCheck(response, profile);
171:
172: if (warningCheck(response)) {
173: log.warn("DIG reasoner warning: "
174: + getWarnings().next());
175: }
176: m_kbURI = null;
177: }
178:
179: // allocate a new KB
180: if (m_kbURI == null) {
181: // request a whole new KB
182: Document response = sendDigVerb(createDigVerb(
183: DIGProfile.NEWKB, profile), profile);
184: errorCheck(response, profile);
185:
186: // extract the new KB URI
187: Element kb = (Element) response.getDocumentElement()
188: .getElementsByTagName(DIGProfile.KB).item(0);
189: if (kb == null) {
190: throw new DIGReasonerException(
191: "Could not locate DIG KB identifier in return value from newKB");
192: } else {
193: m_kbURI = kb.getAttribute(DIGProfile.URI);
194: }
195: }
196: }
197:
198: /**
199: * <p>Check the response from the DIG server to see if there is an error code,
200: * and raise an excption if so.</p>
201: * @param response The response from the DIG server
202: */
203: public void errorCheck(Document response, DIGProfile profile) {
204: Element root = response.getDocumentElement();
205: NodeList errs = root.getElementsByTagName(DIGProfile.ERROR);
206:
207: if (errs != null && errs.getLength() > 0) {
208: Element error = (Element) errs.item(0);
209:
210: String errCode = error.getAttribute(DIGProfile.CODE);
211: int code = (errCode == null || errCode.length() == 0) ? 0
212: : Integer.parseInt(errCode);
213:
214: String msgAttr = error.getAttribute(DIGProfile.MESSAGE);
215:
216: NodeList messages = error.getChildNodes();
217: String message = (messages.getLength() > 0) ? ((Text) messages
218: .item(0)).getNodeValue().trim()
219: : "(no message)";
220:
221: // check for an error indicating an inconsistent KB
222: if (message.equals(profile.getInconsistentKBMessage())) {
223: throw new DIGInconsistentKBException(message, msgAttr,
224: code);
225: } else {
226: throw new DIGErrorResponseException(message, msgAttr,
227: code);
228: }
229: }
230: }
231:
232: /**
233: * <p>Append any warning messages from this response to the list of recent warnings,
234: * which is first cleared.</p>
235: * @param response The response from the DIG server
236: * @return True if any warnings were detected.
237: */
238: public boolean warningCheck(Document response) {
239: Element root = response.getDocumentElement();
240: NodeList ok = root.getElementsByTagName(DIGProfile.OK);
241: m_warnings.clear();
242:
243: if (ok != null && ok.getLength() > 0) {
244: Element e = (Element) ok.item(0);
245: NodeList warnings = e
246: .getElementsByTagName(DIGProfile.WARNING);
247:
248: if (warnings != null && warnings.getLength() > 0) {
249: for (int i = 0; i < warnings.getLength(); i++) {
250: // append the warning message to the list
251: Element warn = (Element) warnings.item(i);
252: m_warnings.add(warn
253: .getAttribute(DIGProfile.MESSAGE));
254: }
255:
256: return true;
257: }
258: }
259:
260: return false;
261: }
262:
263: /**
264: * <p>Answer an iterator over the warnings received since the last tell operation</p>
265: * @return An iterator over warnings
266: */
267: public Iterator getWarnings() {
268: return m_warnings.iterator();
269: }
270:
271: /**
272: * <p>Release this connection back to the connection pool.</p>
273: */
274: public void release() {
275: DIGConnectionPool.getInstance().release(this );
276: }
277:
278: /**
279: * <p>Answer the URL of the external reasoner this connection is bound to.</p>
280: * @return The current external reasoner URL
281: */
282: public String getReasonerURL() {
283: return m_extReasonerURL;
284: }
285:
286: /**
287: * <p>Set the URL of the external reasoner with which this connection communicates.</p>
288: * @param url The URL of the new external reasoner connection point
289: */
290: public void setReasonerURL(String url) {
291: m_extReasonerURL = url;
292: m_kbURI = null;
293: }
294:
295: // Internal implementation methods
296: //////////////////////////////////
297:
298: /**
299: * <p>Answer the XML document object that resulted from the most recent request.</p>
300: * @param conn The current HTTP connection
301: * @return The response from the DIG reasoner, as an XML object
302: * @exception DigReasonerException if the underling connection or XML parser raises
303: * an error.
304: */
305: protected Document getDigResponse(HttpURLConnection conn) {
306: try {
307: // check for successful response
308: if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
309: throw new DIGReasonerException(
310: "DIG reasoner returned failure code "
311: + conn.getResponseCode() + ": "
312: + conn.getResponseMessage());
313: }
314:
315: BufferedInputStream in = new BufferedInputStream(conn
316: .getInputStream());
317: StringBuffer buf = new StringBuffer();
318:
319: // read the return result into a string buffer before we parse it
320: int ch = in.read();
321: while (ch > 0) {
322: buf.append((char) ch);
323: ch = in.read();
324: }
325:
326: // TODO remove LogFactory.getLog( getClass() ).debug( "Response buffer = " + buf.toString() );
327: // now parse into a document
328: DocumentBuilder builder = m_factory.newDocumentBuilder();
329: return builder.parse(new ByteArrayInputStream(buf
330: .toString().getBytes()));
331: } catch (Exception e) {
332: e.printStackTrace(System.err);
333: throw new DIGWrappedException(e);
334: }
335: }
336:
337: /**
338: * <p>Answer an XML formatter object for the given document
339: * @param doc The XML document to be serialised
340: * @return An XML formatter object for the document
341: */
342: protected OutputFormat createXMLFormatter(Document doc) {
343: OutputFormat format = new OutputFormat(doc);
344: format.setIndenting(true);
345: format.setLineWidth(0);
346: format.setPreserveSpace(false);
347:
348: return format;
349: }
350:
351: /**
352: * <p>Create a DIG verb as an xml element in a new document object.</p>
353: * @param verbName The name of the DIG verb, as a string
354: * @return An XML DOM element representing the DIG verb
355: */
356: protected Document createDigVerb(String verbName, DIGProfile profile) {
357: try {
358: // initialise the XML DOM tree
359: DocumentBuilder builder = m_factory.newDocumentBuilder();
360: Document doc = builder.newDocument();
361:
362: // create the verb as the root element of the XML document
363: Element root = doc.createElementNS(profile
364: .getDIGNamespace(), verbName);
365: doc.appendChild(root);
366:
367: // set the standard attributes
368: root.setAttribute("xmlns", profile.getDIGNamespace());
369: root.setAttribute("xmlns:xsi", XSI);
370: root.setAttributeNS(XSI, "xsi:schemaLocation", profile
371: .getDIGNamespace()
372: + " " + profile.getSchemaLocation());
373: if (m_kbURI != null) {
374: root.setAttribute(DIGProfile.URI, m_kbURI);
375: }
376:
377: return doc;
378: } catch (FactoryConfigurationError e) {
379: throw new DIGWrappedException(e);
380: } catch (ParserConfigurationException e) {
381: throw new DIGWrappedException(e);
382: }
383: }
384:
385: /**
386: * <p>Log the messages going to and from DIG</p>
387: * @param outgoing True for send, false for receive
388: * @param msg The document sent or received.
389: */
390: protected void logMessage(boolean outgoing, Document msg) {
391: if (m_logCommunications) {
392: StringWriter out = new StringWriter();
393: serialiseDocument(msg, out);
394:
395: if (log.isDebugEnabled()) {
396: log.debug(outgoing ? "Sending to DIG reasoner ..."
397: : "Received from DIG reasoner ...");
398: log.debug(out);
399: }
400: }
401: }
402:
403: //==============================================================================
404: // Inner class definitions
405: //==============================================================================
406:
407: }
408:
409: /*
410: * (c) Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP
411: * All rights reserved.
412: *
413: * Redistribution and use in source and binary forms, with or without
414: * modification, are permitted provided that the following conditions
415: * are met:
416: * 1. Redistributions of source code must retain the above copyright
417: * notice, this list of conditions and the following disclaimer.
418: * 2. Redistributions in binary form must reproduce the above copyright
419: * notice, this list of conditions and the following disclaimer in the
420: * documentation and/or other materials provided with the distribution.
421: * 3. The name of the author may not be used to endorse or promote products
422: * derived from this software without specific prior written permission.
423: *
424: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
425: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
426: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
427: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
428: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
429: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
430: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
431: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
432: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
433: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
434: */
|