package com.interaction.example.odata.northwind; /* * #%L * interaction-example-odata-northwind * %% * Copyright (C) 2012 - 2013 Temenos Holdings N.V. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ import java.io.BufferedReader; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.StringReader; import java.io.Writer; import java.nio.charset.Charset; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; import javax.persistence.EntityManagerFactory; import javax.ws.rs.core.MediaType; import javax.xml.parsers.DocumentBuilderFactory; import org.junit.Assert; import org.odata4j.edm.EdmSimpleType; import org.odata4j.internal.InternalUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.Text; import org.xml.sax.InputSource; import com.temenos.interaction.test.AbstractRuntimeTest; public abstract class AbstractNorthwindRuntimeTest extends AbstractRuntimeTest { private static final Logger logger = LoggerFactory.getLogger(AbstractNorthwindRuntimeTest.class); protected static final String endpointUri = "http://localhost:8080/northwind/Northwind.svc/"; public AbstractNorthwindRuntimeTest(RuntimeFacadeType type) { super(type); } public static final String RESOURCES_ROOT = "/META-INF/uri-conventions/"; // public static void main(String[] args) throws Exception { // //saveFromNorthwind("/Products(1)/$links/Category", "LinksSingleTest"); // saveFromNorthwind("/Products(1)/$links/Order_Details", "LinksMultipleTest"); // } // private static void saveFromNorthwind(String path, String fileName) { // saveFromNorthwind(path, fileName, MediaType.APPLICATION_XML, "xml"); // saveFromNorthwind(path, fileName, MediaType.APPLICATION_JSON, "json"); // } // private void saveFromNorthwind(String path, String fileName, String accept, String format) { // String northwindSvc = "http://services.odata.org/Northwind/Northwind.svc"; // String contents = this.getWebResource(northwindSvc + path, accept, String.class); // String filePath = "src/test/resources" + RESOURCES_ROOT + format + "/" + fileName + "." + format; // this.writeStringToFile(filePath, contents); // } /* (non-Javadoc) * @see org.odata4j.producer.jpa.northwind.test.NorthwindTestUtils#testJSONResult(java.lang.String, java.lang.String, java.lang.String) */ public void testJSONResult(String endpointUri, String uri, String inp) { System.out.println("Test: " + inp); String RESOURCES_TYPE = "json"; uri = uri.replace(" ", "%20"); String result = this.rtFacade.getWebResource(endpointUri + uri, "application/json"); // different naming result = result.replace( "NorthwindModel.Categories", "NorthwindModel.Category"); result = result.replace( "NorthwindModel.Products", "NorthwindModel.Product"); result = result.replace( "NorthwindModel.Suppliers", "NorthwindModel.Supplier"); result = result.replace( "NorthwindModel.Customers", "NorthwindModel.Customer"); result = result.replace( "NorthwindModel.Order_Details", "NorthwindModel.Order_Detail"); result = result.replace( "http://localhost:8080/northwind", "http://services.odata.org/northwind"); String expect = AbstractNorthwindRuntimeTest.readFileToString( RESOURCES_ROOT + RESOURCES_TYPE + "/" + inp + "." + RESOURCES_TYPE, "ISO-8859-15"); expect = expect.replace( "http://services.odata.org/Northwind", "http://services.odata.org/northwind"); expect = normalizeFormat(expect); result = normalizeFormat(result); String[] resultParts = result.split(","); Arrays.sort(resultParts); String[] expectParts = expect.split(","); Arrays.sort(expectParts); Assert.assertArrayEquals(expectParts, resultParts); } /* (non-Javadoc) * @see org.odata4j.producer.jpa.northwind.test.NorthwindTestUtils#testAtomResult(java.lang.String, java.lang.String, java.lang.String) */ public void testAtomResult(String endpointUri, String uri, String inp) { System.out.println("Test: " + inp); String RESOURCES_TYPE = "xml"; uri = uri.replace(" ", "%20"); String result = this.rtFacade.getWebResource(endpointUri + uri, "application/atom+xml"); result = result.replace( "http://localhost:8080/northwind", "http://services.odata.org/northwind"); result = result.replace("OrderDetails", "Order_Details"); String expect = AbstractNorthwindRuntimeTest.readFileToString( RESOURCES_ROOT + RESOURCES_TYPE + "/" + inp + "." + RESOURCES_TYPE, "utf-8"); expect = expect.replace("http://services.odata.org/Northwind", "http://services.odata.org/northwind"); //System.out.println("result: " + result); try { DocumentBuilderFactory factory = DocumentBuilderFactory .newInstance(); Document expectedDocument = factory.newDocumentBuilder().parse( new InputSource(new StringReader(expect))); Document resultDocument = factory.newDocumentBuilder().parse( new InputSource(new StringReader(result))); assertEquals(expectedDocument.getDocumentElement(), resultDocument.getDocumentElement(), true); } catch (Throwable ex) { System.out.println("expected: "); System.out.println(expect); System.out.println("actual: "); System.out.println(result); ex.printStackTrace(); Assert.fail(ex.toString()); } } public static void assertEquals(Node expected, Node result, boolean isTopLevel) { Assert.assertEquals(expected.getNodeType(), result.getNodeType()); Assert.assertEquals(expected.getNodeName(), result.getNodeName()); Assert.assertEquals(expected.getNamespaceURI(), result.getNamespaceURI()); assertAttributesEquals(expected, result); List<Node> expectedLinks = new ArrayList<Node>(); List<Node> resultLinks = new ArrayList<Node>(); List<Node> expectedProperties = new ArrayList<Node>(); List<Node> resultProperties = new ArrayList<Node>(); List<Node> expectedEntries = new ArrayList<Node>(); List<Node> resultEntries = new ArrayList<Node>(); if (expected.getNodeType() == Node.TEXT_NODE) { Assert.assertEquals(((Text) expected).getData(), ((Text) result).getData()); } int expectedIdx = 0; int resultIdx = 0; while (expectedIdx < expected.getChildNodes().getLength()) { // skip the last newline inside the test data if (expected.getChildNodes().item(expectedIdx).getNodeType() == Node.TEXT_NODE && resultIdx == result.getChildNodes().getLength()) { expectedIdx++; continue; } // skip the newlines inside the test data if (expected.getChildNodes().item(expectedIdx).getNodeType() == Node.TEXT_NODE && result.getChildNodes().item(resultIdx).getNodeType() != Node.TEXT_NODE) { expectedIdx++; Assert.assertTrue(expectedIdx < expected.getChildNodes() .getLength()); } Assert.assertTrue(resultIdx < result.getChildNodes().getLength()); Node expectedChildNode = expected.getChildNodes().item(expectedIdx); Node resultChildNode = result.getChildNodes().item(resultIdx); // The sort order for expanded entries is not defined in the edm // metadata and // may not be defined in the data model. So we do not rely on a // specific order here // and sort the inlined entries before we compare them. if ("m:inline".equals(expectedChildNode.getNodeName())) { isTopLevel = false; } // links and properties can be ordered differently in the expected // and result data if ("link".equals(expectedChildNode.getNodeName())) { // we do not have the Shipper or CustomerDemographics in our // test data, so we ignore it if ("http://schemas.microsoft.com/ado/2007/08/dataservices/related/Shipper" .equals(expectedChildNode.getAttributes() .getNamedItem("rel").getNodeValue()) || "http://schemas.microsoft.com/ado/2007/08/dataservices/related/CustomerDemographics" .equals(expectedChildNode.getAttributes() .getNamedItem("rel").getNodeValue())) { resultIdx--; } else { expectedLinks.add(expectedChildNode); resultLinks.add(resultChildNode); } } else if ("m:properties".equals(expected.getNodeName())) { expectedProperties.add(expectedChildNode); resultProperties.add(resultChildNode); } else if (!isTopLevel && "entry".equals(expectedChildNode.getNodeName())) { // see comment above about m:inline expectedEntries.add(expectedChildNode); resultEntries.add(resultChildNode); } else if ("updated".equals(expected.getNodeName())) ; // ignore because time stamps differ always else if (expected.getAttributes().getNamedItem("m:type") != null && EdmSimpleType.BINARY.getFullyQualifiedTypeName().equals( expected.getAttributes().getNamedItem("m:type") .getNodeValue()) && expectedChildNode.getNodeType() == Node.TEXT_NODE) { // we split the binary data into 76 character blocks, // services.odata.org does not. String s = ((Text) resultChildNode).getData(); s = s.replace("\r", ""); s = s.replace("\n", ""); Assert.assertEquals(((Text) expectedChildNode).getData(), s); // Instead of normalizing the decimal's here (e.g "18.000" // should be equal to "18") // we should probably add precision and scale to EdmType and // regard these // when we write a decimal value } else if (expected.getAttributes().getNamedItem("m:type") != null && EdmSimpleType.DECIMAL.getFullyQualifiedTypeName().equals( expected.getAttributes().getNamedItem("m:type") .getNodeValue()) && expectedChildNode.getNodeType() == Node.TEXT_NODE) { Assert.assertEquals(Double .parseDouble(((Text) expectedChildNode).getData()), Double.parseDouble(((Text) resultChildNode).getData()), 0.00001); } else if (expected.getAttributes().getNamedItem("m:type") != null && EdmSimpleType.SINGLE.getFullyQualifiedTypeName().equals( expected.getAttributes().getNamedItem("m:type") .getNodeValue()) && expectedChildNode.getNodeType() == Node.TEXT_NODE) { // same as with DECIMAL Assert.assertEquals( Float.parseFloat(((Text) expectedChildNode).getData()), Float.parseFloat(((Text) resultChildNode).getData()), 0.00001); } else { if (expected.getAttributes().getNamedItem("m:type") != null && EdmSimpleType.DATETIME.getFullyQualifiedTypeName().equals( expected.getAttributes().getNamedItem("m:type") .getNodeValue()) && expectedChildNode.getNodeType() == Node.TEXT_NODE) { String ed = ((Text) expectedChildNode).getData(); String rd = ((Text) resultChildNode).getData(); Assert.assertEquals(InternalUtil.parseDateTimeFromXml(ed), InternalUtil.parseDateTimeFromXml(rd)); } else { assertEquals(expectedChildNode, resultChildNode, isTopLevel); } } expectedIdx++; resultIdx++; } Assert.assertEquals(expected.getChildNodes().getLength(), expectedIdx); Assert.assertEquals(result.getChildNodes().getLength(), resultIdx); // compare the properties Assert.assertEquals(expectedProperties.size(), resultProperties.size()); assertPropertyNodesEquals(expectedProperties, resultProperties, isTopLevel); // compare the links Assert.assertEquals(expectedLinks.size(), resultLinks.size()); assertLinkNodesEquals(expectedLinks, resultLinks, isTopLevel); // compare the inline entries Assert.assertEquals(expectedEntries.size(), resultEntries.size()); assertInlineEntriesEquals(expectedEntries, resultEntries, isTopLevel); } private static void assertAttributesEquals(Node expected, Node result) { NamedNodeMap expectedAttributes = expected.getAttributes(); NamedNodeMap resultAttributes = result.getAttributes(); // if both are null it's OK if (expectedAttributes != null || resultAttributes != null) { Assert.assertNotNull(expectedAttributes); Assert.assertNotNull(resultAttributes); Assert.assertEquals(expectedAttributes.getLength(), resultAttributes.getLength()); for (int i = 0; i < expectedAttributes.getLength(); i++) { Attr attr = (Attr) resultAttributes .getNamedItem(expectedAttributes.item(i).getNodeName()); Assert.assertNotNull(attr); String expectedValue = ((Attr) expectedAttributes.item(i)) .getValue(); String resultValue = attr.getValue(); // different naming if ("category".equals(expected.getNodeName())) { resultValue = resultValue.replace( "NorthwindModel.Categories", "NorthwindModel.Category"); resultValue = resultValue .replace("NorthwindModel.Products", "NorthwindModel.Product"); resultValue = resultValue.replace( "NorthwindModel.Suppliers", "NorthwindModel.Supplier"); resultValue = resultValue.replace("NorthwindModel.Orders", "NorthwindModel.Order"); resultValue = resultValue.replace( "NorthwindModel.Customers", "NorthwindModel.Customer"); resultValue = resultValue.replace( "NorthwindModel.Order_Details", "NorthwindModel.Order_Detail"); } else if ("link".equals(expected.getNodeName()) && "title".equals(attr.getName()) && "edit".equals(((Attr) expected.getAttributes() .getNamedItem("rel")).getNodeValue())) { resultValue = resultValue.replace("Categories", "Category"); resultValue = resultValue.replace("Products", "Product"); resultValue = resultValue.replace("Suppliers", "Supplier"); resultValue = resultValue.replace("Orders", "Order"); resultValue = resultValue.replace("Customers", "Customer"); resultValue = resultValue.replace("Order_Details", "Order_Detail"); } else if ("link".equals(expected.getNodeName()) && "next".equals(((Attr) expected.getAttributes() .getNamedItem("rel")).getNodeValue()) && "href".equals(attr.getName())) { resultValue = expectedValue; // we are using a different // $skiptoken mechanism } else if ("link".equals(expected.getNodeName()) && ((Attr) expected.getAttributes().getNamedItem("rel")).getNodeValue().startsWith("http://schemas")) { //Ignore lines such as "<link rel="http://schemas..." resultValue = expectedValue; } Assert.assertEquals(expectedValue, resultValue); } } } private static void assertPropertyNodesEquals(List<Node> expected, List<Node> result, boolean isTopLevel) { Assert.assertEquals(expected.size(), result.size()); // sort the properties alphabetically Comparator<Node> comparator = new Comparator<Node>() { @Override public int compare(Node n1, Node n2) { return n1.getNodeName().compareTo(n2.getNodeName()); } }; Collections.sort(expected, comparator); Collections.sort(result, comparator); for (int i = 0; i < expected.size(); i++) { assertEquals(expected.get(i), result.get(i), isTopLevel); } } private static void assertLinkNodesEquals(List<Node> expected, List<Node> result, boolean isTopLevel) { Assert.assertEquals(expected.size(), result.size()); // sort the links by the value of the href attributes if (expected.size() > 1) { Comparator<Node> comparator = new Comparator<Node>() { @Override public int compare(Node n1, Node n2) { if (n1.getAttributes().getNamedItem("href") != null && n2.getAttributes().getNamedItem("href") != null) { return n1 .getAttributes() .getNamedItem("href") .getNodeValue() .compareTo( n2.getAttributes().getNamedItem("href") .getNodeValue()); } else if (n1.getAttributes().getNamedItem("href") != null) { return 1; } else { return -1; } } }; Collections.sort(expected, comparator); Collections.sort(result, comparator); } for (int i = 0; i < expected.size(); i++) { assertEquals(expected.get(i), result.get(i), isTopLevel); } } private static void assertInlineEntriesEquals(List<Node> expected, List<Node> result, boolean isTopLevel) { Assert.assertEquals(expected.size(), result.size()); // sort the entries by id Comparator<Node> comparator = new Comparator<Node>() { @Override public int compare(Node n1, Node n2) { String id1 = getId(n1); String id2 = getId(n2); return id1.compareTo(id2); } private String getId(Node n) { for (int i = 0, len = n.getChildNodes().getLength(); i < len; i++) { if ("id".equals(n.getChildNodes().item(i).getNodeName())) { return n.getChildNodes().item(i).getFirstChild() .getNodeValue(); } } return ""; } }; Collections.sort(expected, comparator); Collections.sort(result, comparator); for (int i = 0; i < expected.size(); i++) { assertEquals(expected.get(i), result.get(i), isTopLevel); } } /** * This method can be used to help track down failures in test cases for the * atom format. */ @SuppressWarnings("unused") private static String toString(Node node) { StringBuilder bld = new StringBuilder(); if (node.getNodeType() == Node.ELEMENT_NODE) { bld.append("<").append(node.getNodeName()).append(" "); for (int i = 0, len = node.getAttributes().getLength(); i < len; i++) { bld.append(node.getAttributes().item(i).getNodeName()).append( "=\""); bld.append(node.getAttributes().item(i).getNodeValue()) .append("\"").append(" "); } bld.append("/>"); } return bld.toString(); } public static String normalizeFormat(String text) { text = text.replace("+", "%20"); text = text.replace("%20", " "); text = text.replace(" ", ""); text = text.replace("\r", ""); text = text.replace("\n", ""); text = text.replace("\\r", ""); text = text.replace("\\n", ""); // no result tag by MS (?) text = text.replace("{\"results\":", ""); // replace braces for ignore fields sort/order in json text = text.replace("}", ""); text = text.replace("]", ""); return text; } public static void fillDatabase(EntityManagerFactory emf) { try { Class.forName("org.hsqldb.jdbcDriver"); } catch (Exception ex) { System.out.println("ERROR: failed to load HSQLDB JDBC driver."); logger.error("ERROR: failed to load HSQLDB JDBC driver.", ex); return; } Connection conn = null; String line = ""; try { conn = DriverManager.getConnection( "jdbc:hsqldb:mem:northwind", "sa", ""); Statement statement = conn.createStatement(); InputStream xml = AbstractNorthwindRuntimeTest.class.getResourceAsStream( "/META-INF/northwind_insert.sql"); BufferedReader br = new BufferedReader( new InputStreamReader(xml, "UTF-8")); while ((line = br.readLine()) != null) { line = line.replace("`", ""); line = line.replace(");", ")"); line = line.replace("'0x", "'"); if (line.length() > 5) { statement.executeUpdate(line); } } br.close(); statement.close(); } catch (Exception ex) { logger.error("There was an error", ex); } finally { if (conn != null) { try { conn.close(); } catch (SQLException ex) { logger.error("There was an error", ex); } } } } /* (non-Javadoc) * @see org.odata4j.producer.jpa.northwind.test.NorthwindTestUtils#writeStringToFile(java.lang.String, java.lang.String) */ public void writeStringToFile(String fileName, String contents) { Writer out = null; try { out = new OutputStreamWriter(new FileOutputStream(fileName), "utf-8"); out.write(contents); } catch (Exception e) { throw new RuntimeException(e); } finally { if (out != null) { try { out.close(); } catch (IOException e) {} } } } public static String readFileToString(String fileName) { return readFileToString(fileName, Charset.defaultCharset().name()); } public static String readFileToString(String fileName, String charsetName) { StringBuilder strBuilder = new StringBuilder(); try { InputStream buf = AbstractNorthwindRuntimeTest.class.getResourceAsStream( fileName); if(buf == null) { throw new Exception("Failed to load file " + fileName); } BufferedReader in = new BufferedReader( new InputStreamReader(buf, charsetName)); String str; try { while ((str = in.readLine()) != null) { strBuilder.append(str); } in.close(); } catch (IOException ex) { logger.error(ex.getMessage(), ex); } } catch (Exception ex) { logger.error(ex.getMessage(), ex); } return strBuilder.toString(); } public String getCount(String endpointUri, String uri) { return this.rtFacade.acceptAndReturn(endpointUri + uri, MediaType.APPLICATION_ATOM_XML_TYPE); } public String getWebResourceAtomXML(String uri) { return this.rtFacade.getWebResource(uri, "application/atom+xml"); } }