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");
}
}