test.junit.fedora.server.search.TestFieldSearchSQLImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of fcrepo-client Show documentation
Show all versions of fcrepo-client Show documentation
The Fedora Client is a Java Library that allows API access to a Fedora Repository. The client is typically one part of a full Fedora installation.
The newest version!
package fedora.server.search;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import junit.framework.JUnit4TestAdapter;
import mock.sql.MockConnection;
import mock.sql.MockDriver;
import mock.sql.MockStatement;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import fedora.server.Context;
import fedora.server.config.DatastoreConfiguration;
import fedora.server.errors.InconsistentTableSpecException;
import fedora.server.errors.ServerException;
import fedora.server.storage.BMechReader;
import fedora.server.storage.ConnectionPool;
import fedora.server.storage.MockBMechReader;
import fedora.server.storage.MockDOReader;
import fedora.server.storage.MockRepositoryReader;
import fedora.server.storage.types.BMechDSBindSpec;
import fedora.server.storage.types.BasicDigitalObject;
import fedora.server.storage.types.DatastreamXMLMetadata;
import fedora.server.storage.types.DigitalObject;
import fedora.server.utilities.SQLUtility;
import fedora.server.utilities.TableCreatingConnection;
import fedora.server.utilities.TableSpec;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.fail;
import static fedora.server.storage.types.DigitalObject.FEDORA_BMECH_OBJECT;
import static fedora.server.storage.types.DigitalObject.FEDORA_OBJECT;
public class TestFieldSearchSQLImpl {
private static final String[] SHORT_FIELDS =
FieldSearchSQLImpl.DB_COLUMN_NAMES_NODC;
private static final String[] LONG_FIELDS =
FieldSearchSQLImpl.DB_COLUMN_NAMES;
private static final String DC_PAYLOAD_NO_DATES =
"\n"
+ " Sandy's Reference Object \n"
+ " Sandy Payette \n"
+ " FOXML Testing \n"
+ " Object depicts all types of datastreams \n"
+ " Cornell CIS \n"
+ " test:100 \n"
+ " \n";
private static final String DC_PAYLOAD_WITH_DATES =
"\n"
+ " Sandy's Reference Object \n"
+ " Sandy Payette \n"
+ " FOXML Testing \n"
+ " Object depicts all types of datastreams \n"
+ " Cornell CIS \n"
+ " test:100 \n"
+ " 2006-10-15 \n" + " \n";
public static junit.framework.Test suite() {
return new JUnit4TestAdapter(TestFieldSearchSQLImpl.class);
}
private static final ObjectData OBJECT_WITH_NO_DC =
new ObjectData("somePid",
"myLabel",
FEDORA_OBJECT,
"cmodeL",
"A",
"theOwner",
new Date(12345),
new Date(67890),
new Date(10000),
null,
null,
null);
private static final ObjectData OBJECT_WITH_DC =
new ObjectData("somePid",
"myLabel",
FEDORA_OBJECT,
"cmodeL",
"A",
"theOwner",
new Date(12345),
new Date(67890),
new Date(10000),
null,
null,
DC_PAYLOAD_NO_DATES);
private static final ObjectData OBJECT_WITH_DC_AND_DATES =
new ObjectData("somePid",
"myLabel",
FEDORA_OBJECT,
"cmodeL",
"A",
"theOwner",
new Date(12345),
new Date(67890),
new Date(10000),
null,
null,
DC_PAYLOAD_WITH_DATES);
private static final ObjectData BMECH_WITH_NO_DC =
new ObjectData("somePid",
"myLabel",
FEDORA_BMECH_OBJECT,
"cmodeL",
"A",
"theOwner",
new Date(12345),
new Date(67890),
new Date(10000),
new String[] {"bmechPid"},
null,
null);
private static final ObjectData BMECH_WITH_DC =
new ObjectData("somePid",
"myLabel",
FEDORA_BMECH_OBJECT,
"cmodeL",
"A",
"theOwner",
new Date(12345),
new Date(67890),
new Date(10000),
new String[] {"bmechWithDC"},
null,
DC_PAYLOAD_NO_DATES);
private static SQLUtility saveSqlUtility;
@BeforeClass
public static void saveSqlUtilityImpl() {
saveSqlUtility = getSqlUtilityInstance();
}
@AfterClass
public static void restoreSqlUtilityImpl() {
setSqlUtilityInstance(saveSqlUtility);
}
private MockConnection mockConnection;
private MockRepositoryReader mockRepositoryReader;
private ConnectionPool connectionPool;
private final MyMockDriver mockDriver = new MyMockDriver();
private int expectedDateInserts;
private int expectedDateDeletes;
@Before
public void registerMockDriver() {
try {
DriverManager.registerDriver(mockDriver);
} catch (SQLException e) {
fail("Failed to register mock JDBC driver: " + e);
}
}
@After
public void deregisterMockDriver() {
try {
DriverManager.deregisterDriver(mockDriver);
} catch (SQLException e) {
fail("Failed to deregister mock JDBC driver: " + e);
}
}
@Before
public void createConnectionPool() throws SQLException {
// Create a connection pool that uses the Mock Driver and some other
// plausible values.
connectionPool =
new ConnectionPool(MockDriver.class.getName(),
"mock://bogus.url",
"bogusUsername",
"bogusPassword",
5,
5,
5,
0,
0,
2,
300,
false,
false,
false,
(byte) 0);
}
@Before
public void clearExpectedValues() {
expectedDateInserts = 0;
expectedDateDeletes = 0;
}
@Test
public void noDC() throws ServerException {
setSqlUtilityInstance(new UnusedMockSqlUtility());
mockConnection = new UnusedMockConnection();
mockRepositoryReader = new UnusedMockRepositoryReader();
updateRecord(OBJECT_WITH_NO_DC, false);
checkExpectations();
}
@Test
public void dcNoDatesShortFields() throws ServerException {
setSqlUtilityInstance(new UpdatingMockSqlUtility(SHORT_FIELDS,
OBJECT_WITH_DC
.getShortFieldValueList()));
mockConnection = new UnusedMockConnection();
mockRepositoryReader = new UnusedMockRepositoryReader();
updateRecord(OBJECT_WITH_DC, false);
checkExpectations();
}
@Test
public void dcNoDatesLongFields() throws ServerException {
setSqlUtilityInstance(new UpdatingMockSqlUtility(LONG_FIELDS,
OBJECT_WITH_DC
.getLongFieldValueList()));
mockConnection = new UpdatingMockConnection();
expectedDateDeletes = 1;
expectedDateInserts = 0;
mockRepositoryReader = new UnusedMockRepositoryReader();
updateRecord(OBJECT_WITH_DC, true);
checkExpectations();
}
@Test
public void dcDatesShortFields() throws ServerException {
setSqlUtilityInstance(new UpdatingMockSqlUtility(SHORT_FIELDS,
OBJECT_WITH_DC_AND_DATES
.getShortFieldValueList()));
mockConnection = new UnusedMockConnection();
mockRepositoryReader = new UnusedMockRepositoryReader();
updateRecord(OBJECT_WITH_DC_AND_DATES, false);
checkExpectations();
}
@Test
public void dcDatesLongFields() throws ServerException {
setSqlUtilityInstance(new UpdatingMockSqlUtility(LONG_FIELDS,
OBJECT_WITH_DC_AND_DATES
.getLongFieldValueList()));
mockConnection = new UpdatingMockConnection();
expectedDateDeletes = 1;
expectedDateInserts = 1;
mockRepositoryReader = new UnusedMockRepositoryReader();
updateRecord(OBJECT_WITH_DC_AND_DATES, true);
checkExpectations();
}
@Test
public void noDcBmech() throws ServerException {
setSqlUtilityInstance(new UnusedMockSqlUtility());
mockConnection = new UnusedMockConnection();
mockRepositoryReader = new BmechMockRepositoryReader("someBdefPid");
updateRecord(BMECH_WITH_NO_DC, false);
checkExpectations();
}
@Test
public void dcNoDatesShortFieldsBmech() throws ServerException {
setSqlUtilityInstance(new UpdatingMockSqlUtility(SHORT_FIELDS,
BMECH_WITH_DC
.getShortFieldValueList()));
mockConnection = new UnusedMockConnection();
mockRepositoryReader = new BmechMockRepositoryReader("bmechWithDC");
updateRecord(BMECH_WITH_DC, false);
checkExpectations();
}
@Test
public void dcNoDatesLongFieldsBmech() throws ServerException {
setSqlUtilityInstance(new UpdatingMockSqlUtility(LONG_FIELDS,
BMECH_WITH_DC
.getLongFieldValueList()));
mockConnection = new UpdatingMockConnection();
expectedDateDeletes = 1;
expectedDateInserts = 0;
mockRepositoryReader = new BmechMockRepositoryReader("bmechWithDC");
updateRecord(BMECH_WITH_DC, true);
checkExpectations();
}
private void updateRecord(ObjectData objectData, boolean longFields)
throws ServerException {
// Create a DC datastream if appropriate.
DatastreamXMLMetadata dcmd = null;
if (objectData.getDcPayload() != null) {
dcmd = new DatastreamXMLMetadata();
dcmd.DatastreamID = "DC";
dcmd.DSCreateDT = objectData.getDcModifiedDate();
dcmd.xmlContent = objectData.getDcPayload().getBytes();
}
// Create the object and populate it.
BasicDigitalObject theObject = new BasicDigitalObject();
theObject.setPid(objectData.getPid());
theObject.setLabel(objectData.getLabel());
theObject.setFedoraObjectType(objectData.getFType());
theObject.setContentModelId(objectData.getCModel());
theObject.setState(objectData.getState());
theObject.setOwnerId(objectData.getOwnerId());
theObject.setCreateDate(objectData.getCreateDate());
theObject.setLastModDate(objectData.getLastModDate());
if (dcmd != null) {
theObject.addDatastreamVersion(dcmd, false);
}
// Create the test instance.
FieldSearchSQLImpl fssi =
new FieldSearchSQLImpl(connectionPool,
mockRepositoryReader,
50,
50,
longFields);
// And do the update.
fssi.update(new MockDOReader(theObject));
}
private void checkExpectations() {
((MockSqlUtility) getSqlUtilityInstance()).checkExpectations();
if (mockConnection instanceof UpdatingMockConnection) {
((UpdatingMockConnection) mockConnection)
.checkExpectations(expectedDateDeletes, expectedDateInserts);
}
if (mockRepositoryReader instanceof BmechMockRepositoryReader) {
((BmechMockRepositoryReader) mockRepositoryReader)
.checkExpectations();
}
}
private void assertEqualArrays(String label,
Object[] expected,
Object[] actual) {
if (!Arrays.equals(expected, actual)) {
fail(label + ", expected: " + Arrays.deepToString(expected)
+ ", actual: " + Arrays.deepToString(actual));
}
}
private void assertEqualValues(String[] columns,
Object[] expected,
Object[] actual) {
if (Arrays.equals(expected, actual)) {
return;
}
String noValue = "_NO_VALUE_";
String message = "";
List badColumns = new ArrayList();
for (int i = 0; i < columns.length; i++) {
Object expectedValue = i < expected.length ? expected[i] : noValue;
Object actualValue = i < actual.length ? actual[i] : noValue;
if (!equivalent(expectedValue, actualValue)) {
badColumns.add(columns[i]);
}
String expectedString =
expectedValue == noValue ? noValue
: expectedValue == null ? "null" : expected[i]
.getClass().getName()
+ "[" + expected[i] + "]";
String actualString =
actualValue == noValue ? noValue
: actualValue == null ? "null" : actual[i]
.getClass().getName()
+ "[" + actual[i] + "]";
message +=
String.format("column '%s', expected=%s, actual=%s\n",
columns[i],
expectedString,
actualString);
}
if (!badColumns.isEmpty()) {
message = "bad columns: " + badColumns + "\n" + message;
}
fail(message);
}
private boolean equivalent(Object o1, Object o2) {
return o1 == null ? o2 == null : o1.equals(o2);
}
/**
* Reach into the {@link SQLUtility} class and get the instance that is
* handling the JDBC-based methods.
*/
private static SQLUtility getSqlUtilityInstance() {
try {
Field instanceField = SQLUtility.class.getDeclaredField("instance");
instanceField.setAccessible(true);
return (SQLUtility) instanceField.get(null);
} catch (SecurityException e) {
fail("Failed to set SqlUtility instance: " + e);
} catch (NoSuchFieldException e) {
fail("Failed to set SqlUtility instance: " + e);
} catch (IllegalArgumentException e) {
fail("Failed to set SqlUtility instance: " + e);
} catch (IllegalAccessException e) {
fail("Failed to set SqlUtility instance: " + e);
}
return null;
}
/**
* Reach into the {@link SQLUtility} class and set an instance to handle the
* JDBC-based methods.
*/
private static void setSqlUtilityInstance(SQLUtility instance) {
try {
Field instanceField = SQLUtility.class.getDeclaredField("instance");
instanceField.setAccessible(true);
instanceField.set(null, instance);
} catch (SecurityException e) {
fail("Failed to set SqlUtility instance: " + e);
} catch (NoSuchFieldException e) {
fail("Failed to set SqlUtility instance: " + e);
} catch (IllegalArgumentException e) {
e.printStackTrace();
fail("Failed to set SqlUtility instance: " + e);
} catch (IllegalAccessException e) {
fail("Failed to set SqlUtility instance: " + e);
}
}
/**
* Base class for Mock {@link SQLUtility} implementations. Every method
* causes the test to fail, unless overridden by the subclass.
*/
public abstract static class MockSqlUtility
extends SQLUtility {
public abstract void checkExpectations();
@Override
protected void i_addRow(Connection conn,
String table,
String[] columns,
String[] values,
boolean[] numeric) throws SQLException {
fail("Unexpected call to MockSqlUtility.i_addRow");
}
@Override
protected void i_createNonExistingTables(ConnectionPool pool,
InputStream dbSpec)
throws IOException, InconsistentTableSpecException,
SQLException {
fail("Unexpected call to MockSqlUtility.i_addRow");
}
@Override
protected void i_createTables(TableCreatingConnection tcConn,
List specs)
throws SQLException {
fail("Unexpected call to MockSqlUtility.i_addRow");
}
@Override
protected ConnectionPool i_getConnectionPool(DatastoreConfiguration cpDC)
throws SQLException {
fail("Unexpected call to MockSqlUtility.i_addRow");
return null;
}
@Override
protected String i_getLongString(ResultSet rs, int pos)
throws SQLException {
fail("Unexpected call to MockSqlUtility.i_addRow");
return null;
}
@Override
protected List i_getNonExistingTables(Connection conn,
List specs)
throws SQLException {
fail("Unexpected call to MockSqlUtility.i_addRow");
return null;
}
@Override
protected void i_replaceInto(Connection conn,
String table,
String[] columns,
String[] values,
String uniqueColumn,
boolean[] numeric) throws SQLException {
fail("Unexpected call to MockSqlUtility.i_addRow");
}
@Override
protected boolean i_updateRow(Connection conn,
String table,
String[] columns,
String[] values,
String uniqueColumn,
boolean[] numeric) throws SQLException {
fail("Unexpected call to MockSqlUtility.i_addRow");
return false;
}
}
public static class UnusedMockSqlUtility
extends MockSqlUtility {
@Override
public void checkExpectations() {
// Nothing to check.
}
}
private class UpdatingMockSqlUtility
extends MockSqlUtility {
private final String[] expectedColumns;
private final String[] expectedValues;
private String[] actualColumns;
private String[] actualValues;
/**
* Write down some of what we expect to have happen.
*
* @param expectedColumns
* @param expectedValues
*/
public UpdatingMockSqlUtility(String[] expectedColumns,
List expectedValues) {
this.expectedColumns = expectedColumns;
this.expectedValues =
expectedValues.toArray(new String[expectedValues.size()]);
}
/**
* If we get a replace call, store the columns and values for testing
* later. (If we get more then one call, only the last will be
* retained.)
*/
@Override
protected void i_replaceInto(Connection conn,
String table,
String[] columns,
String[] values,
String uniqueColumn,
boolean[] numeric) throws SQLException {
actualColumns = columns;
actualValues = values;
}
@Override
public void checkExpectations() {
assertEqualArrays("column names", expectedColumns, actualColumns);
assertEqualValues(expectedColumns, expectedValues, actualValues);
}
}
private static class UnusedMockConnection
extends MockConnection {
@Override
public Statement createStatement() throws SQLException {
fail("Unexpected call to UnusedMockConnection.createStatement");
return null;
}
}
private static class UpdatingMockConnection
extends MockConnection {
private int deleteCalls = 0;
private int insertCalls = 0;
@Override
public Statement createStatement() throws SQLException {
return new MockStatement() {
@Override
public int executeUpdate(String sql) throws SQLException {
if (sql.trim().toLowerCase().startsWith("insert")) {
insertCalls++;
}
if (sql.trim().toLowerCase().startsWith("delete")) {
deleteCalls++;
}
return 1;
}
};
}
public void checkExpectations(int expectedDeletes, int expectedInserts) {
assertEquals("delete calls", expectedDeletes, deleteCalls);
assertEquals("insert calls", expectedInserts, insertCalls);
}
}
private static class UnusedMockRepositoryReader
extends MockRepositoryReader {
@Override
public synchronized BMechReader getBMechReader(boolean cachedObjectRequired,
Context context,
String pid)
throws ServerException {
fail("Unexpected call to UnusedMockRepositoryReader.getBMechReader");
return null;
}
}
private static class BmechMockRepositoryReader
extends MockRepositoryReader {
private final String bdefPid;
private int calls;
public BmechMockRepositoryReader(String bdefPid) {
this.bdefPid = bdefPid;
}
public void checkExpectations() {
assertEquals("bmech reader calls", 1, calls);
}
@Override
public synchronized BMechReader getBMechReader(boolean cachedObjectRequired,
Context context,
String pid)
throws ServerException {
calls++;
return new MockBMechReader(null) {
@Override
public BMechDSBindSpec getServiceDSInputSpec(Date versDateTime)
throws ServerException {
BMechDSBindSpec spec = new BMechDSBindSpec();
spec.bDefPID = bdefPid;
return spec;
}
};
}
}
private class MyMockDriver
extends MockDriver {
@Override
public Connection connect(String url, Properties info)
throws SQLException {
return mockConnection;
}
}
private static class ObjectData {
private final String pid;
private final String label;
private final int fType;
private final String cModel;
private final String state;
private final String ownerId;
private final Date createDate;
private final Date lastModDate;
private final Date dcModifiedDate;
private final String[] bDef;
private final String bMech;
private final String dcPayload;
public ObjectData(String pid,
String label,
int fType,
String cModel,
String state,
String ownerId,
Date createDate,
Date lastModDate,
Date dcModifiedDate,
String[] bDef,
String bMech,
String dcPayload) {
this.pid = pid;
this.label = label;
this.fType = fType;
this.cModel = cModel;
this.state = state;
this.ownerId = ownerId;
this.createDate = createDate;
this.lastModDate = lastModDate;
this.dcModifiedDate = dcModifiedDate;
this.bDef = bDef;
this.bMech = bMech;
this.dcPayload = dcPayload;
}
public List getShortFieldValueList() {
List result = new ArrayList();
result.add(pid);
result.add(lowerCase(label));
result.add(lowerCase(getFtype()));
result.add(lowerCase(cModel));
result.add(lowerCase(state));
result.add(lowerCase(ownerId));
result.add(dateStamp(createDate));
result.add(dateStamp(lastModDate));
result.add(dateStamp(dcModifiedDate));
result.add(joinStrings(bDef));
result.add(bMech);
return result;
}
public List getLongFieldValueList() {
List result = new ArrayList();
result.addAll(getShortFieldValueList());
result.add(lowerCase(getDcFields("dc:title")));
result.add(lowerCase(getDcFields("dc:creator")));
result.add(lowerCase(getDcFields("dc:subject")));
result.add(lowerCase(getDcFields("dc:description")));
result.add(lowerCase(getDcFields("dc:publisher")));
result.add(lowerCase(getDcFields("dc:contributor")));
result.add(lowerCase(getDcFields("dc:date")));
result.add(lowerCase(getDcFields("dc:type")));
result.add(lowerCase(getDcFields("dc:format")));
result.add(lowerCase(getDcFields("dc:identifier")));
result.add(lowerCase(getDcFields("dc:source")));
result.add(lowerCase(getDcFields("dc:language")));
result.add(lowerCase(getDcFields("dc:relation")));
result.add(lowerCase(getDcFields("dc:coverage")));
result.add(lowerCase(getDcFields("dc:rights")));
return result;
}
public String getPid() {
return pid;
}
public String getLabel() {
return label;
}
public int getFType() {
return fType;
}
public String getCModel() {
return cModel;
}
public String getState() {
return state;
}
public String getOwnerId() {
return ownerId;
}
public Date getCreateDate() {
return createDate;
}
public Date getLastModDate() {
return lastModDate;
}
public Date getDcModifiedDate() {
return dcModifiedDate;
}
public String[] getBDef() {
return bDef;
}
public String getBMech() {
return bMech;
}
public String getDcPayload() {
return dcPayload;
}
private String lowerCase(String raw) {
return raw == null ? null : raw.toLowerCase();
}
private String dateStamp(Date date) {
return date == null ? null : String.valueOf(date.getTime());
}
private String getDcFields(String fieldName) {
String pString =
String.format("<%1$s>\\s*([^<]*)\\s*%1$s>", fieldName);
Pattern p = Pattern.compile(pString);
Matcher m = p.matcher(dcPayload);
List values = new ArrayList();
int start = 0;
while (m.find(start)) {
values.add(m.group(1));
start = m.end();
}
return joinStrings(values);
}
private String joinStrings(String[] strings) {
return strings == null ? null : joinStrings(Arrays.asList(strings));
}
private String joinStrings(Collection strings) {
if (strings == null || strings.isEmpty()) {
return null;
}
StringBuffer result = new StringBuffer();
for (String string : strings) {
result.append(" ").append(string).append(" .");
}
return result.toString();
}
private String getFtype() {
if (fType == DigitalObject.FEDORA_OBJECT) {
return "O";
} else {
if (fType == DigitalObject.FEDORA_BMECH_OBJECT) {
return "M";
} else {
return "D";
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy