/* The contents of this file are subject to the license and copyright terms
* detailed in the license directory at the root of the source tree (also
* available online at http://fedora-commons.org/license/).
*/
package org.fcrepo.test.api;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.naming.Context;
import javax.naming.InitialContext;
import junit.framework.JUnit4TestAdapter;
import org.fcrepo.client.FedoraClient;
import org.fcrepo.common.PID;
import org.fcrepo.server.management.FedoraAPIMMTOM;
import org.fcrepo.server.utilities.TypeUtility;
import org.fcrepo.test.FedoraServerTestCase;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.JUnitCore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Performs tests to check notifications provided when management services
* are exercised. Notifications are assumed to be via JMS.
*
* @author Bill Branan
*/
public class TestManagementNotifications
extends FedoraServerTestCase
implements MessageListener {
private static final Logger LOGGER =
LoggerFactory.getLogger(TestManagementNotifications.class);
private static FedoraClient s_client;
private FedoraAPIMMTOM apim;
private final ArrayBlockingQueue<TextMessage> messages = new ArrayBlockingQueue<TextMessage>(10, true);
private final int messageTimeout = 5000; // Maximum number of milliseconds to wait for a message
private Connection jmsConnection;
private Session jmsSession;
private Destination destination;
private MessageConsumer messageConsumer;
public static byte[] dsXML;
public static byte[] demo998FOXMLObjectXML;
static {
// create test xml datastream
StringBuffer sb = new StringBuffer();
sb.append(
"<oai_dc:dc xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:oai_dc=\"http://www.openarchives.org/OAI/2.0/oai_dc/\">");
sb.append("<dc:title>Dublin Core Record</dc:title>");
sb.append("<dc:creator>Author</dc:creator>");
sb.append("<dc:subject>Subject</dc:subject>");
sb.append("<dc:description>Description</dc:description>");
sb.append("<dc:publisher>Publisher</dc:publisher>");
sb.append("<dc:format>MIME type</dc:format>");
sb.append("<dc:identifier>Identifier</dc:identifier>");
sb.append("</oai_dc:dc>");
try {
dsXML = sb.toString().getBytes("UTF-8");
} catch (UnsupportedEncodingException uee) {
}
// create test FOXML object specifying pid=demo:998
sb = new StringBuffer();
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
sb.append(
"<foxml:digitalObject VERSION=\"1.1\" PID=\"demo:998\" xmlns:foxml=\"info:fedora/fedora-system:def/foxml#\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"info:fedora/fedora-system:def/foxml# http://www.fedora.info/definitions/1/0/foxml1-1.xsd\">");
sb.append(" <foxml:objectProperties>");
sb.append(" <foxml:property NAME=\"info:fedora/fedora-system:def/model#state\" VALUE=\"A\"/>");
sb.append(
" <foxml:property NAME=\"info:fedora/fedora-system:def/model#label\" VALUE=\"Image of Coliseum in Rome\"/>");
sb.append(
" <foxml:property NAME=\"info:fedora/fedora-system:def/model#createdDate\" VALUE=\"2004-12-10T00:21:57Z\"/>");
sb.append(
" <foxml:property NAME=\"info:fedora/fedora-system:def/view#lastModifiedDate\" VALUE=\"2004-12-10T00:21:57Z\"/>");
sb.append(" </foxml:objectProperties>");
sb.append("</foxml:digitalObject>");
try {
demo998FOXMLObjectXML = sb.toString().getBytes("UTF-8");
} catch (UnsupportedEncodingException uee) {
}
}
@BeforeClass
public static void bootStrap() throws Exception {
s_client = getFedoraClient();
// demo:14
ingestDocumentTransformDemoObjects(s_client);
}
@AfterClass
public static void cleanUp() throws Exception {
purgeDemoObjects(s_client);
s_client.shutdown();
}
@Before
public void setUp() throws Exception {
apim = s_client.getAPIMMTOM();
// Create and start a subscriber
Properties props = new Properties();
props.setProperty(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.activemq.jndi.ActiveMQInitialContextFactory");
props.setProperty(Context.PROVIDER_URL, "tcp://localhost:61616");
props.setProperty("topic.notificationTopic", "fedora.apim.update");
Context jndi = new InitialContext(props);
ConnectionFactory jmsConnectionFactory =
(ConnectionFactory) jndi.lookup("ConnectionFactory");
jmsConnection = jmsConnectionFactory.createConnection();
jmsSession = jmsConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
destination = (Topic) jndi.lookup("notificationTopic");
messageConsumer = jmsSession.createConsumer(destination);
messageConsumer.setMessageListener(this);
jmsConnection.start();
}
@After
public void tearDown() throws Exception {
jmsConnection.stop();
jmsSession.close();
jmsConnection.close();
}
/**
* Tests notifications on
* 1) ingest
* 2) modifyObject
* 3) addRelationship
* 4) purgeRelationship
* 5) purgeObject
*
* @throws Exception
*/
@Test
public void testObjectMethodNotifications() throws Exception {
// (1) test ingest
LOGGER.info("Running TestManagementNotifications.testIngest...");
String pid =
apim.ingest(TypeUtility.convertBytesToDataHandler(demo998FOXMLObjectXML),
FOXML1_1.uri,
"ingesting new foxml object");
assertNotNull(pid);
// Check on the notification produced by ingest
checkNotification(pid, "ingest");
// (2) test modifyObject
LOGGER.info("Running TestManagementNotifications.testModifyObject...");
String modifyResult =
apim.modifyObject(pid,
"I",
"Updated Object Label",
null,
"Changed state to inactive and updated label");
assertNotNull(modifyResult);
// Check on the notification produced by modifyObject
checkNotification(pid, "modifyObject");
// (3a) test addRelationship - pid
LOGGER.info("Running TestManagementNotifications.testAddRelationship...");
boolean addRelResult =
apim.addRelationship(pid,
"rel:isRelatedTo",
"demo:5",
false,
null);
assertTrue(addRelResult);
// Check on the notification produced by addRelationship
checkNotification(pid, "addRelationship");
// (3b) test addRelationship - object uri
LOGGER.info("Running TestManagementNotifications.testAddRelationship...");
addRelResult =
apim.addRelationship(PID.toURI(pid),
"rel:isRelatedTo",
"demo:6",
false,
null);
assertTrue(addRelResult);
// Check on the notification produced by addRelationship
checkNotification(pid, "addRelationship");
// (3c) test addRelationship - datastream uri
LOGGER.info("Running TestManagementNotifications.testAddRelationship...");
addRelResult =
apim.addRelationship(PID.toURI(pid) + "/DS1",
"rel:isRelatedTo",
"demo:7",
false,
null);
assertTrue(addRelResult);
// Check on the notification produced by addRelationship
checkNotification(pid, "addRelationship");
// (4a) test purgeRelationship - pid
LOGGER.info("Running TestManagementNotifications.testPurgeRelationship...");
boolean purgeRelResult =
apim.purgeRelationship(pid,
"rel:isRelatedTo",
"demo:5",
false,
null);
assertTrue(purgeRelResult);
// Check on the notification produced by purgeRelationship
checkNotification(pid, "purgeRelationship");
// (4b) test purgeRelationship - object uri
LOGGER.info("Running TestManagementNotifications.testPurgeRelationship...");
purgeRelResult =
apim.purgeRelationship(PID.toURI(pid),
"rel:isRelatedTo",
"demo:6",
false,
null);
assertTrue(purgeRelResult);
// Check on the notification produced by purgeRelationship
checkNotification(pid, "purgeRelationship");
// (4c) test purgeRelationship - datastream uri
LOGGER.info("Running TestManagementNotifications.testPurgeRelationship...");
purgeRelResult =
apim.purgeRelationship(PID.toURI(pid) + "/DS1",
"rel:isRelatedTo",
"demo:7",
false,
null);
assertTrue(purgeRelResult);
// Check on the notification produced by purgeRelationship
checkNotification(pid, "purgeRelationship");
// (5) test purgeObject
LOGGER.info("Running TestManagementNotifications.testPurgeObject...");
String purgeResult = apim.purgeObject(pid, "Purging object " + pid, false);
assertNotNull(purgeResult);
// Check on the notification produced by purgeObject
checkNotification(pid, "purgeObject");
}
/**
* Test notifications on
* 1) addDatastream
* 2) modifyDatastreamByReference
* 3) modifyDatastreamByValue
* 4) setDatastreamState
* 5) setDatastreamVersionable
* 6) purgeDatastream
*
* @throws Exception
*/
@Test
public void testDatastreamMethodNotifications() throws Exception {
// (1) test addDatastream
LOGGER.info("Running TestManagementNotifications.testAddDatastream...");
String[] altIds = new String[1];
altIds[0] = "Datastream Alternate ID";
String pid = "demo:14";
String datastreamId =
apim.addDatastream(pid,
"NEWDS1",
TypeUtility.convertStringtoAOS(altIds),
"A New M-type Datastream",
true,
"text/xml",
"info:myFormatURI/Mtype/stuff#junk",
getBaseURL() + "/get/fedora-system:ContentModel-3.0/DC",
"M",
"A",
null,
null,
"adding new datastream");
// test that datastream was added
assertEquals(datastreamId, "NEWDS1");
// Check on the notification produced by addDatastream
checkNotification(pid, "addDatastream");
datastreamId =
apim.addDatastream(pid,
"NEWDS2",
TypeUtility.convertStringtoAOS(altIds),
"A New X-type Datastream",
true,
"text/xml",
"info:myFormatURI/Mtype/stuff#junk",
getBaseURL() + "/get/fedora-system:ContentModel-3.0/DC",
"X",
"A",
null,
null,
"adding new datastream");
// test that datastream was added
assertEquals(datastreamId, "NEWDS2");
// Check on the notification produced by addDatastream
checkNotification(pid, "addDatastream");
// (2) test modifyDatastreamByReference
LOGGER.info("Running TestManagementNotifications.testModifyDatastreamByReference...");
String updateTimestamp =
apim.modifyDatastreamByReference(pid,
"NEWDS1",
TypeUtility.convertStringtoAOS(altIds),
"Modified Datastream by Reference",
"text/xml",
"info:newMyFormatURI/Mtype/stuff#junk",
getBaseURL() + "/get/fedora-system:ContentModel-3.0/DC",
null,
null,
"modified datastream by reference notification test",
false);
// test that method returned properly
assertNotNull(updateTimestamp);
// Check on the notification produced by modifyDatastreamByReference
checkNotification(pid, "modifyDatastreamByReference");
// (3) test modifyDatastreamByValue
LOGGER.info("Running TestManagementNotifications.testModifyDatastreamByValue...");
updateTimestamp =
apim.modifyDatastreamByValue(pid,
"NEWDS2",
TypeUtility.convertStringtoAOS(altIds),
"Modified Datastream by Value",
"text/xml",
"info:newMyFormatURI/Xtype/stuff#junk",
TypeUtility.convertBytesToDataHandler(dsXML),
null,
null,
"modified datastream by value notification test",
false);
// test that method returned properly
assertNotNull(updateTimestamp);
// Check on the notification produced by modifyDatastreamByValue
checkNotification(pid, "modifyDatastreamByValue");
// (4) test setDatastreamState
LOGGER.info("Running TestManagementNotifications.testSetDatastreamState...");
String setStateresult =
apim.setDatastreamState(pid,
"NEWDS1",
"I",
"Changed state of datstream DC to Inactive");
assertNotNull(setStateresult);
// Check on the notification produced by setDatastreamState
checkNotification(pid, "setDatastreamState");
// (5) test setDatastreamVersionable
LOGGER.info("Running TestManagementNotifications.testSetDatastreamVersionable...");
String setVersionableResult =
apim.setDatastreamVersionable(pid,
"NEWDS2",
false,
"Changed versionable on datastream NEWDS1 to false");
assertNotNull(setVersionableResult);
// Check on the notification produced by setDatastreamVersionable
checkNotification(pid, "setDatastreamVersionable");
// (5) test purgeDatastream
LOGGER.info("Running TestManagementNotifications.testPurgeDatastream...");
List<String> results =
apim.purgeDatastream(pid,
"NEWDS1",
null,
null,
"purging datastream NEWDS1",
false);
assertTrue(results.size() > 0);
// Check on the notification produced by purgeDatastream
checkNotification(pid, "purgeDatastream");
results =
apim.purgeDatastream(pid,
"NEWDS2",
null,
null,
"purging datastream NEWDS2",
false);
assertTrue(results.size() > 0);
// Check on the notification produced by purgeDatastream
checkNotification(pid, "purgeDatastream");
}
@Test
public void testSelectors() throws Exception {
LOGGER.info("Running TestManagementNotifications.testSelectors...");
messageConsumer.close();
String messageSelector = "methodName LIKE 'ingest%'";
messageConsumer = jmsSession.createConsumer(destination, messageSelector);
messageConsumer.setMessageListener(this);
// Ingest - message should be delivered
String pid =
apim.ingest(TypeUtility.convertBytesToDataHandler(demo998FOXMLObjectXML),
FOXML1_1.uri,
"ingesting new foxml object");
assertNotNull(pid);
checkNotification(pid, "ingest");
// Purge - message selector should prevent message from being delivered
String purgeResult = apim.purgeObject(pid, "Purging object " + pid, false);
assertNotNull(purgeResult);
checkNoNotifications();
}
/**
* Waits for a notification message and checks to see if the message
* body includes the includedText.
*
* @param methodName - the text that should be found in the message body
*/
private void checkNotification(String pid, String methodName) throws Exception {
//messageNumber++;
TextMessage message = messages.poll(messageTimeout, TimeUnit.MILLISECONDS);
if (message == null) {
fail("Timeout reached waiting for notification " +
"on message regarding: " + methodName);
}
String failureText = "Notification <<" + message.getText() +
">> did not include text: " + methodName;
assertTrue(failureText, message.getText().contains(methodName));
failureText = "Notification <<" + message.getStringProperty("methodName") +
">> did not include methodName property with " +
"value: " + methodName;
assertTrue(failureText,
methodName.equals(message.getStringProperty("methodName")));
failureText = "Notification did not include pid property with " +
"value: " + pid;
assertTrue(failureText,
pid.equals(message.getStringProperty("pid")));
//currentMessage = null;
}
/**
* Waits for a notification to make sure none come through.
*/
private void checkNoNotifications() throws Exception {
TextMessage message = messages.poll(messageTimeout, TimeUnit.MILLISECONDS);
if (message != null) {
fail("No messages should be received during this test.");
}
//currentMessage = null;
}
/**
* Handles messages sent as notifications.
* {@inheritDoc}
*/
@Override
public void onMessage(Message msg) {
if (msg instanceof TextMessage) {
//currentMessage = (TextMessage) msg;
messages.add((TextMessage)msg);
//messageCount++;
}
}
public static junit.framework.Test suite() {
return new JUnit4TestAdapter(TestManagementNotifications.class);
}
public static void main(String[] args) {
JUnitCore.runClasses(TestManagementNotifications.class);
}
}