/******************************************************************************* | |
* Copyright (c) 1998, 2013 Oracle and/or its affiliates. All rights reserved. | |
* This program and the accompanying materials are made available under the | |
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 | |
* which accompanies this distribution. | |
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html | |
* and the Eclipse Distribution License is available at | |
* http://www.eclipse.org/org/documents/edl-v10.php. | |
* | |
* Contributors: | |
* Oracle - initial API and implementation from Oracle TopLink | |
******************************************************************************/ | |
package org.eclipse.persistence.testing.tests.jpa.customfeatures; | |
import java.io.*; | |
import java.sql.DatabaseMetaData; | |
import java.sql.SQLException; | |
import java.util.List; | |
import org.w3c.dom.*; | |
import javax.xml.parsers.*; | |
import javax.xml.transform.stream.StreamResult; | |
import javax.persistence.EntityManager; | |
import junit.framework.*; | |
import org.eclipse.persistence.testing.framework.TestCase; | |
import org.eclipse.persistence.testing.framework.junit.JUnitTestCase; | |
import org.eclipse.persistence.testing.models.jpa.customfeatures.*; | |
import org.eclipse.persistence.tools.schemaframework.PackageDefinition; | |
import org.eclipse.persistence.tools.schemaframework.StoredProcedureDefinition; | |
import org.eclipse.persistence.tools.schemaframework.SchemaManager; | |
import org.eclipse.persistence.internal.databaseaccess.Accessor; | |
import org.eclipse.persistence.internal.helper.Helper; | |
import org.eclipse.persistence.platform.xml.XMLPlatformFactory; | |
import org.eclipse.persistence.platform.xml.XMLTransformer; | |
import org.eclipse.persistence.sessions.DatabaseSession; | |
import org.eclipse.persistence.sessions.Session; | |
import org.eclipse.persistence.sessions.server.ServerSession; | |
public class CustomFeaturesJUnitTestSuite extends JUnitTestCase { | |
private static int empId; | |
protected static int NUM_INSERTS = 200; | |
public static String dbVersion; | |
public CustomFeaturesJUnitTestSuite() { | |
super(); | |
} | |
public CustomFeaturesJUnitTestSuite(String name) { | |
super(name); | |
} | |
public static Test suite() { | |
TestSuite suite = new TestSuite(); | |
suite.setName("CustomFeaturesJUnitTestSuite"); | |
suite.addTest(new CustomFeaturesJUnitTestSuite("testSetup")); | |
suite.addTest(new CustomFeaturesJUnitTestSuite("testNCharXMLType")); | |
suite.addTest(new CustomFeaturesJUnitTestSuite("testBatchInserts")); | |
suite.addTest(new CustomFeaturesJUnitTestSuite("testBatchUpdates")); | |
suite.addTest(new CustomFeaturesJUnitTestSuite("testNamedStoredProcedureInOutQuery")); | |
suite.addTest(new CustomFeaturesJUnitTestSuite("testNamedStoredProcedureCursorQuery")); | |
return suite; | |
} | |
public void testSetup() throws SQLException { | |
ServerSession session = JUnitTestCase.getServerSession("customfeatures"); | |
new EmployeeTableCreator().replaceTables(session); | |
buildOraclePackage(session); | |
buildOracleStoredProcedureReadFromEmployeeInOut(session); | |
buildOracleStoredProcedureReadFromEmployeeCursor(session); | |
Accessor accessor = session.getDefaultConnectionPool().acquireConnection(); | |
try { | |
accessor.incrementCallCount(session); | |
DatabaseMetaData metaData = accessor.getConnection().getMetaData(); | |
String dbMajorMinorVersion = Integer.toString(metaData.getDatabaseMajorVersion()) + '.' + Integer.toString(metaData.getDatabaseMinorVersion()); | |
String dbProductionVersion = metaData.getDatabaseProductVersion(); | |
// For Helper.compareVersions to work the first digit in the passed version String should be part of the version, | |
// i.e. "10.2.0.2 ..." is ok, but "Oracle 10g ... 10.2.0.2..." is not. | |
dbVersion = dbProductionVersion.substring(dbProductionVersion.indexOf(dbMajorMinorVersion)); | |
} finally { | |
accessor.decrementCallCount(); | |
session.getDefaultConnectionPool().releaseConnection(accessor); | |
} | |
} | |
/** | |
* Tests a NChar and XML Type with Document. | |
*/ | |
public void testNCharXMLType() { | |
EntityManager em = createEntityManager("customfeatures"); | |
beginTransaction(em); | |
Employee emp = null; | |
try { | |
emp = new Employee(); | |
emp.setResume_xml(resume0()); | |
char nCh = '\u0410'; | |
emp.setEmpNChar(nCh); | |
emp.setResume_dom(documentFromString(resume0())); | |
em.persist(emp); | |
empId = emp.getId(); | |
commitTransaction(em); | |
} catch (RuntimeException e) { | |
if (isTransactionActive(em)) { | |
rollbackTransaction(em); | |
} | |
closeEntityManager(em); | |
throw e; | |
} | |
try { | |
em.clear(); | |
clearCache("customfeatures"); | |
if(isOnServer()) { | |
beginTransaction(em); | |
} | |
Employee readEmp = em.find(Employee.class, empId); | |
compare(readEmp, emp); | |
} finally { | |
if (isTransactionActive(em)) { | |
rollbackTransaction(em); | |
} | |
closeEntityManager(em); | |
} | |
} | |
/** | |
* Tests a Native Batch Writing as batch inserts. | |
*/ | |
public void testBatchInserts() { | |
EntityManager em = createEntityManager("customfeatures"); | |
beginTransaction(em); | |
try { | |
for (int i = 0; i < NUM_INSERTS; i++) { | |
Employee emp = new Employee(); | |
emp.setResume_xml(resume0()); | |
emp.setResume_dom(documentFromString(resume0())); | |
char nCh = '\u0410'; | |
emp.setEmpNChar(nCh); | |
em.persist(emp); | |
} | |
commitTransaction(em); | |
} finally { | |
if (isTransactionActive(em)) { | |
rollbackTransaction(em); | |
} | |
closeEntityManager(em); | |
} | |
} | |
/** | |
* Tests a Native Batch Writing as batch updates with | |
* OptimisticLockingException. | |
*/ | |
public void testBatchUpdates() { | |
EntityManager em = createEntityManager("customfeatures"); | |
beginTransaction(em); | |
List emps = em.createQuery("SELECT OBJECT(e) FROM Employee e").getResultList(); | |
try { | |
for (int i = 0; i < emps.size(); i++) { | |
Employee e = (Employee) emps.get(i); | |
String newName = ((Employee) emps.get(i)).getName() + i + "test"; | |
e.setName(newName); | |
e.setVersion(e.getVersion() - 1); | |
} | |
em.flush(); | |
commitTransaction(em); | |
fail("OptimisticLockingException is not thrown!"); | |
} catch (Exception exception) { | |
if (exception.getMessage().indexOf("org.eclipse.persistence.exceptions.OptimisticLockException") == -1) { | |
fail("it's not the right exception"); | |
} | |
} finally { | |
if (isTransactionActive(em)) { | |
rollbackTransaction(em); | |
} | |
closeEntityManager(em); | |
} | |
} | |
/** | |
* Tests a @NamedStoredProcedureQuery with store procedure IN_OUT parameter, | |
* and XML Type using String | |
*/ | |
public void testNamedStoredProcedureInOutQuery() { | |
if(Helper.compareVersions(dbVersion, "11.2.0.2") < 0) { | |
// Oracle db 11.2.0.2 or later is required for this test | |
return; | |
} | |
EntityManager em = createEntityManager("customfeatures"); | |
beginTransaction(em); | |
try { | |
Employee emp = new Employee(); | |
emp.setResume_xml(resume1()); | |
emp.setResume_dom(documentFromString(resume0())); | |
char nCh = '\u0400'; | |
emp.setEmpNChar(nCh); | |
em.persist(emp); | |
commitTransaction(em); | |
em.clear(); | |
clearCache("customfeatures"); | |
if(isOnServer()) { | |
beginTransaction(em); | |
} | |
// note that readEmployee will have only two attributes set: id and empNChar | |
Employee readEmp = (Employee) em.createNamedQuery("ReadEmployeeInOut").setParameter("ID", emp.getId()).getSingleResult(); | |
if (emp.getEmpNChar() != readEmp.getEmpNChar()) { | |
fail("readEmp.getEmpNChar() == " + readEmp.getEmpNChar() + ", does not match empNChar of the object that was written: " + emp.getEmpNChar()); | |
} | |
} finally { | |
if (isTransactionActive(em)) { | |
rollbackTransaction(em); | |
} | |
closeEntityManager(em); | |
} | |
} | |
/** | |
* Tests a @NamedStoredProcedureQuery with store procedure ref Cursor, and | |
* XML Type using String | |
*/ | |
public void testNamedStoredProcedureCursorQuery() { | |
EntityManager em = createEntityManager("customfeatures"); | |
beginTransaction(em); | |
try { | |
Employee emp = new Employee(); | |
emp.setResume_xml(resume1()); | |
emp.setResume_dom(documentFromString(resume0())); | |
char nCh = '\u0400'; | |
emp.setEmpNChar(nCh); | |
emp.setName("Edward Xu"); | |
em.persist(emp); | |
commitTransaction(em); | |
em.clear(); | |
clearCache("customfeatures"); | |
if(isOnServer()) { | |
beginTransaction(em); | |
} | |
Employee readEmp = (Employee) em.createNamedQuery("ReadEmployeeCursor").setParameter("ID", emp.getId()).getSingleResult(); | |
compare(readEmp, emp); | |
} finally { | |
if (isTransactionActive(em)) { | |
rollbackTransaction(em); | |
} | |
closeEntityManager(em); | |
} | |
} | |
public static String resume0() { | |
String resume = "<resume>\n"; | |
resume += " <first-name>Bob</first-name>\n"; | |
resume += " <last-name>Jones</last-name>\n"; | |
resume += " <age>45</age>\n"; | |
resume += " <education>\n"; | |
resume += " <degree>BCS</degree>\n"; | |
resume += " <degree>MBA</degree>\n"; | |
resume += " </education>\n"; | |
resume += "</resume>"; | |
return resume; | |
} | |
public static String resume1() { | |
String resume = "<resume>\n"; | |
resume += " <first-name>Frank</first-name>\n"; | |
resume += " <last-name>Cotton</last-name>\n"; | |
resume += " <age>27</age>\n"; | |
resume += " <education>\n"; | |
resume += " <degree>BCS</degree>\n"; | |
resume += " </education>\n"; | |
resume += "</resume>"; | |
return resume; | |
} | |
public static Document documentFromString(String xmlString) { | |
try { | |
ByteArrayInputStream stream = new ByteArrayInputStream(xmlString.getBytes()); | |
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); | |
DocumentBuilder builder = factory.newDocumentBuilder(); | |
Document doc = builder.parse(stream); | |
return doc; | |
} catch (Exception ex) { | |
fail("Unable to create document due to: " + ex.getMessage()); | |
} | |
return null; | |
} | |
public PackageDefinition buildOraclePackage(Session session) { | |
if (TestCase.supportsStoredProcedures(session)) { | |
PackageDefinition types = new PackageDefinition(); | |
types.setName("Cursor_Type"); | |
types.addStatement("Type Any_Cursor is REF CURSOR"); | |
SchemaManager schema = new SchemaManager(((DatabaseSession) session)); | |
schema.replaceObject(types); | |
return types; | |
} else { | |
fail("store procedure is not supported!"); | |
return null; | |
} | |
} | |
public void buildOracleStoredProcedureReadFromEmployeeInOut(Session session) { | |
if (TestCase.supportsStoredProcedures(session)) { | |
StoredProcedureDefinition proc = new StoredProcedureDefinition(); | |
proc.setName("Read_Employee_InOut"); | |
proc.addInOutputArgument("employee_id_v", Integer.class); | |
proc.addOutputArgument("nchar_v", "NCHAR"); | |
String statement = "SELECT NCHARTYPE INTO nchar_v FROM CUSTOM_FEATURE_EMPLOYEE WHERE (ID = employee_id_v)"; | |
proc.addStatement(statement); | |
SchemaManager schema = new SchemaManager(((DatabaseSession) session)); | |
schema.replaceObject(proc); | |
} else | |
fail("store procedure is not supported!"); | |
} | |
public void buildOracleStoredProcedureReadFromEmployeeCursor(Session session) { | |
if (TestCase.supportsStoredProcedures(session)) { | |
StoredProcedureDefinition proc = new StoredProcedureDefinition(); | |
proc.setName("Read_Employee_Cursor"); | |
proc.addArgument("employee_id_v", Integer.class); | |
proc.addOutputArgument("RESULT_CURSOR", "CURSOR_TYPE.ANY_CURSOR"); | |
proc.addStatement("OPEN RESULT_CURSOR FOR SELECT * FROM CUSTOM_FEATURE_EMPLOYEE WHERE (ID = employee_id_v)"); | |
SchemaManager schema = new SchemaManager(((DatabaseSession) session)); | |
schema.replaceObject(proc); | |
} else | |
fail("store procedure is not supported!"); | |
} | |
/* | |
* This method is necessary because of a bug in Oracle xdb 11.2.0.2: XDB - 11.2.0.2 DB FORMATS RETURNED XML | |
* This bug describes the following workaround: | |
* In init.ora, add "31151 trace name context forever, level 0x100" | |
* When the bug is fixed (or 11.2.0.2 db configurured as described in the workaround)) | |
* the special case for 11.2.0.2 Oracle db should be removed: | |
* | |
* void compare(Employee readEmp, Employee emp) { | |
* if (!getServerSession("customfeatures").compareObjects(readEmp, emp)) { | |
* fail("Object: " + readEmp + " does not match object that was written: " + emp + ". See log (on finest) for what did not match."); | |
* } | |
* } | |
*/ | |
void compare(Employee readEmp, Employee emp) { | |
if(Helper.compareVersions(dbVersion, "11.2.0.2") >= 0) { | |
// Oracle db 11.2.0.2 returns formatted xml, therefore the original and the read back strings might differ. | |
String originalReadResume_xml = null; | |
String originalResume_xml = null; | |
if(!readEmp.getResume_xml().equals(emp.getResume_xml())) { | |
originalReadResume_xml = readEmp.getResume_xml(); | |
originalResume_xml = emp.getResume_xml(); | |
String unformattedReadResume_xml = removeWhiteSpaceFromString(originalReadResume_xml); | |
String unformattedResume_xml = removeWhiteSpaceFromString(originalResume_xml); | |
if(unformattedReadResume_xml.equals(unformattedResume_xml)) { | |
// xml docs defined by the two strings are equivalent | |
// temporary remove the strings from their owner Employees so that it could pass compareObjects | |
readEmp.setResume_xml(null); | |
emp.setResume_xml(null); | |
} else { | |
fail("unformattedReadResume_xml == " + unformattedReadResume_xml + "\nunformattedResume_xml == " + unformattedResume_xml); | |
} | |
} | |
// Oracle db 11.2.0.2 returns formatted xml, therefore the original and the read back doms might differ. | |
Document originalReadResume_dom = null; | |
Document originalResume_dom = null; | |
if(!readEmp.getResume_dom().equals(emp.getResume_dom())) { | |
originalReadResume_dom = readEmp.getResume_dom(); | |
originalResume_dom = emp.getResume_dom(); | |
Document unformattedReadResume_dom = (Document)originalReadResume_dom.cloneNode(true); | |
removeEmptyTextNodes(unformattedReadResume_dom); | |
Document unformattedResume_dom = (Document)originalResume_dom.cloneNode(true); | |
removeEmptyTextNodes(unformattedResume_dom); | |
String unformattedReadResume_dom_toString = convertDocumentToString(unformattedReadResume_dom); | |
String unformattedResume_dom_toString = convertDocumentToString(unformattedResume_dom); | |
if(unformattedReadResume_dom_toString.equals(unformattedResume_dom_toString)) { | |
// xml docs defined by the two strings are equivalent | |
// temporary remove the doms from their owner Employees so that it could pass compareObjects | |
readEmp.setResume_dom(null); | |
emp.setResume_dom(null); | |
} else { | |
fail("unformattedReadResume_dom_toString == " + unformattedReadResume_dom_toString + "\nunformattedResume_dom_toString == " + unformattedResume_dom_toString); | |
} | |
} | |
try { | |
if (!getServerSession("customfeatures").compareObjects(readEmp, emp)) { | |
fail("Object: " + readEmp + " does not match object that was written: " + emp + ". See log (on finest) for what did not match."); | |
} | |
} finally { | |
if(emp.getResume_xml() == null && originalResume_xml != null) { | |
// set back the temporary removed resume_xml into both objects | |
readEmp.setResume_xml(originalReadResume_xml); | |
emp.setResume_xml(originalResume_xml); | |
} | |
if(emp.getResume_dom() == null && originalResume_dom != null) { | |
// set back the temporary removed resume_dom into both objects | |
readEmp.setResume_dom(originalReadResume_dom); | |
emp.setResume_dom(originalResume_dom); | |
} | |
} | |
} else { | |
// Before version 11.2.0.2 Oracle db returned xml string exactly as it was written (keeping the same format: white spaces, \r, \n etc). | |
// No special comparison for resume_xml is required. | |
if (!getServerSession("customfeatures").compareObjects(readEmp, emp)) { | |
fail("Object: " + readEmp + " does not match object that was written: " + emp + ". See log (on finest) for what did not match."); | |
} | |
} | |
} | |
// Contributed by Blaise | |
public static void removeEmptyTextNodes(Node node) { | |
NodeList nodeList = node.getChildNodes(); | |
Node childNode; | |
for (int x = nodeList.getLength() - 1; x >= 0; x--) { | |
childNode = nodeList.item(x); | |
if (childNode.getNodeType() == Node.TEXT_NODE) { | |
if (childNode.getNodeValue().trim().equals("")) { | |
node.removeChild(childNode); | |
} | |
} else if (childNode.getNodeType() == Node.ELEMENT_NODE) { | |
removeEmptyTextNodes(childNode); | |
} | |
} | |
} | |
// Contributed by Blaise | |
public static String removeWhiteSpaceFromString(String s) { | |
String returnString = s.replaceAll(" ", ""); | |
returnString = returnString.replaceAll("\n", ""); | |
returnString = returnString.replaceAll("\t", ""); | |
returnString = returnString.replaceAll("\r", ""); | |
return returnString; | |
} | |
static String convertDocumentToString(Document doc) { | |
XMLTransformer xmlTransformer = XMLPlatformFactory.getInstance().getXMLPlatform().newXMLTransformer(); | |
StringWriter writer = new StringWriter(); | |
StreamResult result = new StreamResult(writer); | |
xmlTransformer.transform(doc, result); | |
return writer.getBuffer().toString(); | |
} | |
} |