package gov.nist.registry.ws.test; import gov.nist.registry.common2.exception.ExceptionUtil; import gov.nist.registry.common2.exception.XdsInternalException; import gov.nist.registry.common2.io.Io; import gov.nist.registry.common2.registry.MetadataSupport; import gov.nist.registry.common2.xml.Util; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FilterInputStream; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.io.PrintWriter; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.xml.namespace.QName; import junit.framework.TestCase; import org.apache.axiom.om.OMElement; public class RetrieveDocumentSetTest extends TestCase { HashMap<String, String> header_fields; String query_response; String eol = "\r\n"; StringBuffer parse_log; public enum MsgLevel { INFO (0), WARN (1), ERROR (2), FATAL (3), EXCEPT(4); private final int value; MsgLevel(int value) { this.value = value; } public boolean printable(MsgLevel l) { return l.value >= value; } }; MsgLevel level = MsgLevel.ERROR; int error_count = 0; void info(String message) { if (level.printable(MsgLevel.INFO)) parse_log.append("\n[info ] " + message); } void warn(String message) { if (level.printable(MsgLevel.WARN)) parse_log.append("\n[warn ] " + message); } void error(String message) { if (level.printable(MsgLevel.ERROR)) {parse_log.append("\n[error] " + message); error_count++; }} void fatal(String message) throws Exception { if (level.printable(MsgLevel.EXCEPT)) {parse_log.append("\n[fatal] " + message); throw new Exception("See error log"); }} void except(Exception e) { parse_log.append("\n[Exception] " + ExceptionUtil.exception_details(e)); } void resetErrors() { error_count = 0; } boolean hasErrors() { return error_count != 0; } public String getLog() { return parse_log.toString(); } public RetrieveDocumentSetTest() { parse_log = new StringBuffer(); } class NoMessage extends Message { String mtom_errors, xop_errors, errors; public NoMessage(String mtom_errors, String xop_errors, String errors) { super(); this.mtom_errors = mtom_errors; this.xop_errors = xop_errors; this.errors = errors; } } public Message call(File sendSoapMessageFile, String host, String port, String service) throws Exception { try { OMElement sendMessage = Util.parse_xml(sendSoapMessageFile); basic_call(host, port, service, sendMessage.toString()); if (query_response.length() == 0) fail("empty response"); info("Response Message starts here"); Message message = new Message(header_fields, query_response); info("Message is " + message.toString()); resetErrors(); XopMessage xop = null; try { xop = new XopMessage(message); } catch (Exception e) { } if ( xop != null && !hasErrors()) return xop; StringBuffer xop_parse_log = parse_log; resetErrors(); MtomMessage mtom = null; try { mtom = new MtomMessage(message); } catch (Exception e) { } if ( mtom != null && !hasErrors()) return mtom; StringBuffer mtom_parse_log = parse_log; return new NoMessage(mtom_parse_log.toString(), xop_parse_log.toString(), ""); } catch (XdsInternalException e) { fail("RetrieveDocumentSetTest:send() failed : " + ExceptionUtil.exception_details(e)); return new NoMessage("","", ExceptionUtil.exception_details(e)); } } private void basic_call(String host, String port, String service, String body) throws Exception { URL url; HttpURLConnection conn = null; OutputStream os = null; try { url = new URL("http", host, Integer.parseInt(port), service); conn = (HttpURLConnection) url.openConnection(); conn.setDoOutput(true); conn.setUseCaches(false); conn.setRequestMethod("POST"); conn.setRequestProperty("Accept", "text/html, text/xml"); conn.setRequestProperty("Content-Type", "application/soap+xml; charset=UTF-8; action=\"urn:ihe:iti:2007:RetrieveDocumentSet\""); conn.connect(); os = conn.getOutputStream(); os.write(body.getBytes()); } catch (Exception e) { except(e); } InputStream in = null; try { String encoding =conn.getContentEncoding(); if (encoding == null) { in = (FilterInputStream) conn.getInputStream(); } else { Object o = conn.getContent(); in = (FilterInputStream) o; } } catch (java.io.IOException e) { int code = conn.getResponseCode(); //System.out.println("ERROR: code: " + String.valueOf(code) + " message: " + conn.getResponseMessage()); InputStream is = conn.getErrorStream(); if (is == null) { String msg = conn.getResponseMessage(); URL url2 = conn.getURL(); fatal("Error retieving content of " + url2.toString() + "; response was " + msg); } else { StringBuffer b = new StringBuffer(); byte[] by = new byte[256]; while ( is.read(by, 0, 256) > 0 ) b.append(new String(by)); // get junk at end, should be sensitive to number of bytes read //System.out.println(new String(b)); fatal("ERROR: HttpClient: code: " + String.valueOf(code) + " message: " + conn.getResponseMessage() + "\n" + new String(b) + "\n"); } } Map<String, List<String>> x_header_fields = conn.getHeaderFields(); header_fields = new HashMap<String, String>(); Set<String> entrySet = x_header_fields.keySet(); // int i = entrySet.size(); // String x = ""; // ArrayList<String> foo = new ArrayList<String>(); for (String key : entrySet) { if (key == null) continue; List<String> values = x_header_fields.get(key); String value = values.get(0); header_fields.put(key.toLowerCase(), value); // x += key + "\t" + value + "\n"; } query_response = Io.getStringFromInputStream(in); info("Start of raw data"); info("Received headers: " + header_fields.toString()); info("Received body: " + query_response); info("End of raw data"); } class Header { String name; String value; HashMap<String, String> parms; boolean is_valid; public String toString() { String answer = name + ": " + value; if (parms != null) for (String parmname : parms.keySet()) { answer += "; " + parmname + "=" + parms.get(parmname); } return answer; } public Header(String s) { parms = new HashMap<String, String>(); is_valid = false; String h[] = s.split(":", 2); if (h.length != 2) return; name = h[0].trim().toLowerCase(); String values[] = h[1].split(";"); for (int i=0; i<values.length; i++) { if (i == 0) { value = values[i].trim(); info("header " + name + " = " + value); is_valid = true; continue; } String pieces[] = values[i].split("="); if (pieces.length == 2) { String parm_name = pieces[0].trim().toLowerCase(); String parm_value = pieces[1].trim(); if (parm_value.length() > 1 && parm_value.charAt(0) == '"' && parm_value.charAt(parm_value.length()-1) == '"') { parm_value = parm_value.substring(1, parm_value.length() - 1); } parms.put(parm_name, parm_value); info("\t" + parm_name + " = " + parm_value); } } info("New Header: " + toString()); } public Header(String name, String parts) { this.name = name; parms = new HashMap<String, String>(); is_valid = false; // all the data is in parts[0], strange String[] values = parts.split(";"); for (int i=0; i<values.length; i++) { if (i == 0) { value = values[i].trim(); info("header " + name + " = " + value); is_valid = true; continue; } String apart = values[i]; if (apart != null) { String pieces[] = apart.split("="); if (pieces.length == 2) { String parm_name = pieces[0].trim().toLowerCase(); String parm_value = pieces[1].trim(); if (parm_value.length() > 1 && parm_value.charAt(0) == '"' && parm_value.charAt(parm_value.length()-1) == '"') { parm_value = parm_value.substring(1, parm_value.length() - 1); } parms.put(parm_name, parm_value); info("\t" + parm_name + " = " + parm_value); } } } info("New Header: " + toString()); } } class Part { HashMap<String, Header> headers; String body; boolean is_valid = false; String content_id; public String toString() { String answer = "Part: \n"; for (String headername : headers.keySet()) { answer += headers.get(headername).toString() + "\n"; } return answer; } public Part(String input, int part_headers_start_at, int body_ends_at) { parse(input, part_headers_start_at, body_ends_at); } public Part(String input) { int start = 0; if (input.startsWith("HTTP")) { start = input.indexOf(eol) + eol.length(); info("Skipping HTTP line"); } parse(input, start, input.length()-1); } public Part(HashMap<String, String> in_headers, String body) { headers = new HashMap<String, Header>(); for (String header_name : in_headers.keySet()) { String header_value = in_headers.get(header_name); addHeader(header_name, header_value); } warn("Part:"); warn("in_headers: " + in_headers.toString()); warn("headers: " + headers.values().toString()); this.content_id = getHeaderValue("Content-ID"); this.body = body; this.is_valid = true; warn("Body size is " + this.body.length()); info("Part is valid"); } private void parse(String input, int part_headers_start_at, int body_ends_at) { headers = new HashMap<String, Header>(); if (input.indexOf(eol) == -1) { error("No eol characters found"); return; } int part_headers_end_at = input.indexOf(eol+eol, part_headers_start_at); info("Found part headers ending at " + part_headers_end_at); if (part_headers_end_at == -1) return; int body_starts_at = part_headers_end_at + (eol+eol).length(); info("Body start at " + body_starts_at); if (body_starts_at == -1) return; parseHeaders(input.substring(part_headers_start_at, part_headers_end_at)); content_id = getHeaderValue("Content-ID"); body = input.substring(body_starts_at, body_ends_at); is_valid = true; info("Part is valid"); } void addHeader(String s) { Header h = new Header(s); if (h.is_valid) headers.put(h.name, h); } void addHeader(String s, String values) { Header h = new Header(s, values); if (h.is_valid) headers.put(h.name, h); } private void parseHeaders(String headers_text) { String headers[] = headers_text.split(eol); for (int i=0; i<headers.length; i++) { addHeader(headers[i]); } } public boolean hasHeader(String name) { return headers.get(name) != null; } public String getHeaderValue(String name) { try { return headers.get(name.toLowerCase()).value; } catch (Exception e) { return null; } } public String getHeaderParmValue(String name, String parm) { try { Header hdr = headers.get(name.toLowerCase()); String val = hdr.parms.get(parm.toLowerCase()); return val; } catch (Exception e) { return null; } } public String getHeadersAsString() { return headers.toString(); } } class Multipart { ArrayList<Part> parts; public String toString() { String answer = "Multipart: "; for (Part p : parts) { answer += " " + p.content_id; } return answer; } public Multipart(String input, String boundary) { parts = new ArrayList<Part>(); if ( !boundary.startsWith("--")) boundary = "--" + boundary; int index = 0; while(true) { int boundary_starts_at = input.indexOf(boundary, index); int next_boundary_starts_at = input.indexOf(boundary, boundary_starts_at + boundary.length()); info("Found boundary starting at " + boundary_starts_at); if (boundary_starts_at == -1) break; int part_headers_start_at = endOfLine(input, boundary_starts_at); info("Found part headers starting at " + part_headers_start_at); if (part_headers_start_at == -1) break; info("Next boundary starts at " + next_boundary_starts_at); if (next_boundary_starts_at == -1) break; int body_ends_at = next_boundary_starts_at - 1; info("Body ends at " + body_ends_at); Part part = new Part(input, part_headers_start_at, body_ends_at); if (part.is_valid) parts.add(part); index = next_boundary_starts_at; } } public Part getPart(int i) { return parts.get(i); } public Part getPartByContentId(String id) { id = "<" + id + ">"; for (int i=0; i<parts.size(); i++) { Part part = parts.get(i); if (part.content_id != null && part.content_id.equals(id)) return part; } return null; } } class Message { Part all; String boundary; Multipart mp; String start; Part start_part; public Message() { } public Message(Message m) { all = m.all; boundary = m.boundary; mp = m.mp; start = m.start; start_part = m.start_part; } public String toString() { String answer = "Message:" + "\n\tBoundary = " + boundary + "\n\tMultipart = " + ((mp == null) ? "null" : mp.toString()) + "\n\tStart = " + start + "\n\tall = " + all.toString(); return answer; } public Message(String input) { parse(input); } public Message(String input, String _eol) { parse(input, _eol); } public Message(HashMap<String, String> headers, String body) { info("Parsing entire message using Part algorithm"); all = new Part(headers, body); warn("Created Message from Part - part is " + all.toString()); parseAsMultipart(body); } public void parse(String input, String _eol) { eol = _eol; parse(input); } public void parse(String input) { info("Parsing entire message using Part algorithm"); all = new Part(input); parseAsMultipart(input); } public void parseAsMultipart(String input) { boundary = all.getHeaderParmValue("Content-Type", "boundary"); mp = (boundary != null) ? new Multipart(input, boundary) : null; if (boundary != null && mp.parts.size() == 0) { error("Message has a boundary header but parser found no Parts"); mp = null; } if (mp != null) { start = all.getHeaderParmValue("Content-Type", "start"); info("Start is " + start); if (start != null) { start_part = mp.getPartByContentId(start); if (start_part == null) error("Header has start parameter but no matching Part was found"); else info("Start Part found by start parameter in header"); } else { start_part = mp.getPart(0); info("First Part used as start part"); } } if (mp == null) info("Message is not a mulitpart"); else info("Message is a multipart with " + mp.parts.size() + " parts"); } public String getBody() { if (mp == null) return all.body; else return start_part.body; } public Part getPartByContentId(String id) { return mp.getPartByContentId(id); } } class MtomMessage extends Message { public MtomMessage(String input) throws Exception { super(input); validate(); } public MtomMessage(String input, String _eol) throws Exception { super(input, _eol); validate(); } public MtomMessage(Message m) throws Exception { super(m); validate(); } private void validate() throws Exception { if (boundary != null || mp != null) { fatal("MTomMessage: should not have a boundary part in Content-Type header; Should not parse as multi-part"); } else { String body = getBody(); OMElement soap_message = null; try { soap_message = Util.parse_xml(body); } catch (Exception e) { fatal("MtomMessage: SOAP message does not parse as XML"); } List<OMElement> soapBodies = MetadataSupport.decendentsWithLocalName(soap_message, "RetrieveDocumentSetResponse"); if (soapBodies.size() > 0) { List<OMElement> documents = MetadataSupport.decendentsWithLocalName(soapBodies.get(0), "Document"); for (OMElement document : documents) { List<OMElement> includes = MetadataSupport.childrenWithLocalName(document, "Include"); if (includes.size() != 0) error("MtomMessage: should not have Include element inside Document element - looks like MTOM/XOP instead of MTOM"); } } } } } class XopMessage extends Message { public XopMessage(String input) throws Exception { super(input); validate(); } public XopMessage(String input, String _eol) throws Exception { super(input, _eol); validate(); } public XopMessage(Message m) throws Exception { super(m); validate(); } private void validate() throws Exception { if (boundary == null || mp == null) { fatal("XopMessage: should have a boundary part in Content-Type header and should parse as multi-part"); } else { String body = getBody(); OMElement soap_message = null; try { soap_message = Util.parse_xml(body); } catch (Exception e) { fatal("XopMessage: SOAP message does not parse as XML"); } List<OMElement> soapBodies = MetadataSupport.decendentsWithLocalName(soap_message, "RetrieveDocumentSetResponse"); if (soapBodies.size() > 0) { List<OMElement> documents = MetadataSupport.decendentsWithLocalName(soapBodies.get(0), "Document"); for (OMElement document : documents) { List<OMElement> includes = MetadataSupport.childrenWithLocalName(document, "Include"); if (includes.size() == 0) fatal("XopMessage: should have Include element inside Document element - looks like MTOM instead of MTOM/XOP"); else if (includes.size() > 1) fatal("XopMessage: should have only one Include element inside Document element."); OMElement include = includes.get(0); String href = include.getAttributeValue(new QName("href")); if (href == null || href.equals("")) fatal("XopMessage: no href attribute inside Include element"); if (href.startsWith("cid:")) href = href.replaceFirst("cid:", ""); else error("XopMessage: href must start with 'cid'"); Part doc = this.getPartByContentId(href); if (doc == null) error("XopMessage: Part with content id of " + href + " not found in message"); } } } } } int endOfLine(String line, int fromIndex) { int nexteol = line.indexOf(eol, fromIndex); if (nexteol == -1) return -1; return nexteol + eol.length(); } public void xtest_parseMultipart1() { String boundary = "abc"; String in = ""; Multipart mp = new Multipart(in, boundary); assertTrue(mp.parts.size() == 0); } public void xtest_parseMultipart2() { String boundary = "abc"; String boundary2 = "--" + boundary; String in = boundary2 + eol + "a: aa" + eol + eol + "the body" + boundary2; Multipart mp = new Multipart(in, boundary); assertTrue(mp.parts.size() == 1); assertTrue(mp.getPart(0).hasHeader("a")); assertFalse(mp.getPart(0).hasHeader("v")); } public void xtest_parseMultipart3() { String boundary = "abc"; String boundary2 = "--" + boundary; String in = boundary2 + eol + "a: aa" + eol + eol + "the body" + boundary2 + eol; Multipart mp = new Multipart(in, boundary); assertTrue(mp.parts.size() == 1); } public void xtest_parseMultipart4() { String boundary = "abc"; String boundary2 = "--" + boundary; String in = boundary2 + eol + "a: aa" + eol + "b: aa" + eol + eol + "the body\nmore body" + boundary2; Multipart mp = new Multipart(in, boundary); assertTrue(mp.parts.size() == 1); } public void xtest_parseMultipart5() { String boundary = "abc"; String boundary2 = "--" + boundary; eol = "\n"; String in = boundary2 + eol + "a: aa" + eol + "b: aa" + eol + eol + "the body\nmore body" + boundary2; Multipart mp = new Multipart(in, boundary); assertTrue(mp.parts.size() == 1); } public void xtest_parseMultipart6() { String boundary = "abc"; String boundary2 = "--" + boundary; String in = "" + boundary2 + eol + "a: aa" + eol + "b: aa" + eol + eol + "the body\nmore body" + boundary2 + "a: aa" + eol + "b: aa" + eol + eol + "the body\nmore body" + boundary2 ; Multipart mp = new Multipart(in, boundary); assertTrue(mp.parts.size() == 2); } public void xtest_parseMultipart7() throws Exception { String boundary = "MIMEBoundaryurn_uuid_3BE45FAC62CF0568A81199380869988"; String boundary2 = "--" + boundary; eol = "\n"; String in = Io.getStringFromInputStream(new FileInputStream(new File("/Users/bill/dev/xds/testdata/RetrieveDocumentSet/sample1.txt"))); Multipart mp = new Multipart(in, boundary); assertTrue("found " + mp.parts.size() + " parts - expecting 2\n" + getLog(), mp.parts.size() == 2); } public void xtest_parseMultipart8() throws Exception { eol = "\n"; String in = Io.getStringFromInputStream(new FileInputStream(new File("/Users/bill/dev/xds/testdata/RetrieveDocumentSet/sample1.txt"))); Part all = new Part(in); String boundary = all.getHeaderParmValue("Content-Type", "boundary"); assertNotNull(all.getHeadersAsString(), boundary); Multipart mp = new Multipart(in, boundary); assertTrue("found " + mp.parts.size() + " parts - expecting 2\n" + getLog(), mp.parts.size() == 2); } public void xtest_parseMultipart9() throws Exception { resetErrors(); String in = Io.getStringFromInputStream(new FileInputStream(new File("/Users/bill/dev/xds/testdata/RetrieveDocumentSet/sample1.txt"))); Message msg = new Message(in, "\n"); System.out.println(this.parse_log.toString()); assertTrue(msg.mp != null); assertTrue("found " + msg.mp.parts.size() + " parts - expecting 2\n" + getLog(), msg.mp.parts.size() == 2); assertTrue(msg.start.equals("0.urn:uuid:3BE45FAC62CF0568A81199380869989@apache.org")); assertTrue(msg.mp.getPartByContentId("0.urn:uuid:3BE45FAC62CF0568A81199380869989@apache.org") != null); assertFalse(msg.start_part == null); assertFalse(hasErrors()); } public void xtest_parseMultipart10() throws Exception { resetErrors(); String in = Io.getStringFromInputStream(new FileInputStream(new File("/Users/bill/dev/xds/testdata/RetrieveDocumentSet/sample1.txt"))); new XopMessage(in, "\n"); System.out.println(this.parse_log.toString()); assertFalse(hasErrors()); } public void xtest_parseMultipart11() throws Exception { resetErrors(); String in = Io.getStringFromInputStream(new FileInputStream(new File("/Users/bill/dev/xds/testdata/RetrieveDocumentSet/sample2.txt"))); new MtomMessage(in, "\n"); System.out.println(this.parse_log.toString()); assertFalse(hasErrors()); } public void xtest_parseMultipart12() throws Exception { resetErrors(); String local = ""; Message m = null; try { m = call(new File("/Users/bill/dev/xds/testdata/RetrieveDocumentSet/sample3.txt"), "localhost", "9080", "/axis2xop/services/xdsrepositoryb"); } catch (Exception e) { local = ExceptionUtil.exception_details(e); } // OutputStream os = new FileOutputStream("/Users/bill/tmp/out.txt"); // PrintWriter pw = new PrintWriter(os); PrintStream pw = System.out; if (m instanceof NoMessage) { NoMessage n = (NoMessage) m; pw.println("\n\nXOP\n\n" + n.xop_errors); pw.println("\n\nMTOM\n\n" + n.mtom_errors); pw.println("\n\nOTHER\n\n" + n.errors); } if (! local.equals("")) pw.println("\n\nLOCAL\n\n" + local); pw.flush(); // os.flush(); pw.close(); // os.close(); assertTrue(m.getClass().getName(), m instanceof XopMessage); } public void test_parseMultipart13() throws Exception { resetErrors(); String local = ""; Message m = null; try { m = call(new File("/Users/bill/dev/xds/testdata/RetrieveDocumentSet/sample3.txt"), "localhost", "9080", "/axis2/services/xdsrepositoryb"); } catch (Exception e) { local = ExceptionUtil.exception_details(e); } // OutputStream os = new FileOutputStream("/Users/bill/tmp/out.txt"); // PrintWriter pw = new PrintWriter(os); PrintStream pw = System.out; if (m instanceof NoMessage) { NoMessage n = (NoMessage) m; pw.println("\n\nXOP\n\n" + n.xop_errors); pw.println("\n\nMTOM\n\n" + n.mtom_errors); pw.println("\n\nOTHER\n\n" + n.errors); } if (! local.equals("")) pw.println("\n\nLOCAL\n\n" + local); pw.flush(); // os.flush(); pw.close(); // os.close(); assertTrue(m.getClass().getName(), m instanceof MtomMessage); } }