package org.openeai.moa;
import java.util.ArrayList;
import java.util.Properties;
import org.jdom.Element;
import org.jdom.Document;
import org.jdom.JDOMException;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import org.openeai.config.EnterpriseFields;
import org.openeai.config.MessageObjectConfig;
import org.openeai.layouts.EnterpriseLayoutException;
import org.openeai.transport.*;
import org.openeai.moa.objects.resources.*;
import org.openeai.transport.ProducerId;
import org.openeai.xml.XmlDocumentReaderException;
import org.openeai.xml.XmlValidator;
public abstract class ActionableEnterpriseObjectBase
extends XmlEnterpriseObjectImpl implements ActionableEnterpriseObject {
private java.util.List m_lastErrors = null;
private MessageId m_messageId = null;
private Authentication m_authentication = null;
private String m_commandName = "";
public ActionableEnterpriseObjectBase() {
}
/**
* Helper method that returns this object as an XmlEnterpriseObject.
*<P>
* @return org.openeai.moa.XmlEnterpriseObject. This object cast to an XmlEnterpriseObject.
**/
public XmlEnterpriseObject getXmlEnterpriseObject() {
return (XmlEnterpriseObject)this;
}
/**
* Create message production. Builds an XML document (using the primed create document
* as a "template") out of the current contents of the object. While building the
* XML Create document, it will validate contents of the object checking
* for any malformed, missing or invalid fields. Field data values are also
* checked for validitity against the EnterpriseObjects document. Uses the
* producer passed in to send the XML document as a JMS request to the queue
* connected to by the producer which then processes the response checking
* for any errors in the response.
*<P>
* @param producer PointToPointProducer a pre-configured and started PointToPointProducer
* which will be used to send the create-request message to the appropriate destination
* and return the reply to this method (via the PointToPointProducer.produceRequest method).
*<P>
* @return XmlEnterpriseObject a generic response
* which will indicate success or failure. If failure, any error information will
* be included in that result. The object returned is actually a
* org.openeai.moa.objects.Result object.
*<P>
* @throws EnterpriseObjectCreateException if any errors occur when validating
* the contents of the object, if any errors occur while producing the request
* or if the object doesn't support the create action. This exception will also
* be thrown if the contents of the reply document contains an error. That is, if
* the consuming application had errors processing the request. This exception will
* include the Result information containing the error that occurred.
*/
public synchronized final XmlEnterpriseObject create(RequestService producer)
throws EnterpriseObjectCreateException {
try {
setSenderInfo("Create", producer);
Document doc = getCreateDoc();
buildCreateMessage(doc);
return processRequest("Create", producer, doc);
}
catch (Exception e) {
String errMessage = "Error processing the create request for object " +
getClass().getName() + " Exception: " + e.getMessage();
throw new EnterpriseObjectCreateException(errMessage, e);
}
}
/**
* Create Sync message production. Builds an XML document (using the primed
* create sync document as a baseline) out of the current contents of the object.
* While building the XML Create Sync document, it will validate contents of
* the object checking for any malformed, missing or invalid fields.
* Field data values are also checked for validitity against the
* EnterpriseObjects document. Publishes the XML document in a JMS message
* to the topic connected to by producer.
*
* @param producer org.openeai.jms.producer.PubSubProducer to
* use to publish the message.
*
* @throws EnterpriseObjectSyncException if any errors occur when validating
* the contents of the object. Or, if any errors occur while publishing the message.
*/
public synchronized final void createSync(SyncService producer)
throws EnterpriseObjectSyncException {
try {
setSenderInfo("CreateSync", producer);
Document doc = getCreateSyncDoc();
buildCreateMessage(doc);
processSync("CreateSync", producer, doc );
}
catch (Exception e) {
String errMessage = "Error producing the createSync message for object " +
getClass().getName() + " Exception: " + e.getMessage();
throw new EnterpriseObjectSyncException(errMessage, e);
}
}
/**
* Using the current contents of the object along with the 'primed' XML document
* this method builds the acutal Enterprise Message. (e.g. - Person/BasicPerson-Create-Request or Person/BasicPerson-Create-Sync).
* This Enterprise Message will then be sent via JMS to a queue/topic and consumed
* by a Gateway which will process the request/sync and return a reply if appropriate. Note,
* replies will only be returned when a request message is sent.
*
*<P>
* @param createDoc Document the 'primed' xml document for this action. (e.g. - a sample BasicPerson-Create-Request.xml document).
*<P>
* @return Document the Enterprise Message with a DataArea consisting of the current contents of this object and a ControlArea
* consisting of appropriate ControlArea information based on the configuration of this object. (e.g. - Sender, Datetime etc.)
*<P>
* @throws EnterpriseObjectCreateException if errors serializing the object as an Xml Element.
* @throws JDOMException if errors occur manipulating the document.
*/
protected void buildCreateMessage(Document createDoc)
throws JDOMException, EnterpriseObjectCreateException {
if (createDoc == null) {
throw new EnterpriseObjectCreateException("No primed Create document. Can't continue.");
}
logger.debug("buildCreateMessage, createDoc: " + createDoc.toString());
// Set the elements/attributes of the doc for a create
// Control area
Element controlArea = getControlArea(createDoc.getRootElement());
// Data area
Element dataArea = createDoc.getRootElement().getChild("DataArea");
Element oldElement = dataArea.getChild("NewData").getChild(getElementName());
Element newElement = null;
try {
logger.debug("[buildCreateMessage] Number of output layout managers: " + getOutputLayoutManagers().size());
logger.debug("[buildCreateMessage] Output Layout Manager is " + getOutputLayoutManager().getClass().getName());
newElement = (Element)buildOutputFromObject();
}
catch (EnterpriseLayoutException e) {
logger.fatal("Error building createMessage for object " + getClass().getName() + " content of object: " + toString());
String errMessage = "Error building createMessage for object " +
getClass().getName() + " Exception: " + e.getMessage();
throw new EnterpriseObjectCreateException(errMessage, e);
}
dataArea.getChild("NewData").removeChild(getElementName());
dataArea.getChild("NewData").addContent(newElement);
// Add the Sender, Datetime and ExpectedReplyFormat to the ControlArea
try {
controlArea = setControlArea(controlArea);
}
catch (EnterpriseObjectException e) {
String errMessage = "[JmsEnterpriseObject] Exception setting control area of Create document. Exception: " + e.getMessage();
logger.fatal(errMessage);
throw new EnterpriseObjectCreateException(errMessage, e);
}
}
/**
* Update message production. Builds an XML document (using the primed update document
* as a "template") out of the current contents of the object (this). It will use the
* current contents of the object as the NewData portion of the message and it will
* use this object's "baseline" object as the Baseline portion of the message. The "baseline"
* object is set when this object was "queried" for previously.
* While building the XML Update message, it will validate contents checking
* for any malformed, missing or invalid fields. Field data values are also
* checked for validitity against the EnterpriseObjects document. Uses the
* producer passed in to send the XML document as a JMS request to the queue
* connected to by the producer which then processes the response checking
* for any errors in the response.
*<P>
* @param producer PointToPointProducer a pre-configured and started PointToPointProducer
* which will be used to send the update-request message to the appropriate destination
* and return the reply to this method (via the PointToPointProducer.produceRequest method).
*<P>
* @return XmlEnterpriseObject (Result) this will indicate the success or failure
* of the Update request. In an error condition, this will include any error
* information.
*<P>
* @throws EnterpriseObjectUpdateException if any errors occur when validating
* the contents of the object, if any errors occur while producing the request
* or if the object doesn't support the create action. This exception will also
* be thrown if the contents of the reply document contains an error. That is, if
* the consuming application had errors processing the request. This exception will
* include the Result information containing the error that occurred.
*/
public synchronized final XmlEnterpriseObject update(RequestService producer)
throws EnterpriseObjectUpdateException {
try {
setSenderInfo("Update", producer);
Document doc = getUpdateDoc();
buildUpdateMessage(doc);
return processRequest("Update", producer, doc);
}
catch (Exception e) {
String errMessage = "Error processing the update request for object " +
getClass().getName() + " Exception: " + e.getMessage();
throw new EnterpriseObjectUpdateException(errMessage, e);
}
}
/**
* Update Sync message production. Builds an XML document (using the primed
* update sync document as a "template") out of the current contents of the object.
* It will use the current contents of the object as the NewData portion of the message and it will
* use the m_baseline object as the Baseline portion of the message. The m_baseline
* object is set when this object was "queried" for previously.
* While building the XML Update Sync document, it will validate contents of
* the object checking for any malformed, missing or invalid fields.
* Field data values are also checked for validitity against the
* EnterpriseObjects document. Publishes the XML document in a JMS message
* to the topic connected to by producer.
*<P>
* @param producer PubSubProducer a pre-configured and started PubSubProducer
* which will be used to send the delete-sync message to the appropriate destination
* (via the PubSubProducer.publishMessage method).
*<P>
* @throws EnterpriseObjectSyncException if any errors occur when validating
* the contents of the object. Or, if any errors occur while publishing the message.
*/
public synchronized final void updateSync(SyncService producer)
throws EnterpriseObjectSyncException {
try {
setSenderInfo("UpdateSync", producer);
Document doc = getUpdateSyncDoc();
buildUpdateMessage(doc);
processSync("UpdateSync", producer, doc);
}
catch (Exception e) {
String errMessage = "Error producing the updateSync message for object " +
getClass().getName() + " Exception: " + e.getMessage();
throw new EnterpriseObjectSyncException(errMessage, e);
}
}
/**
* Using the current contents of the object along with the 'primed' XML document
* this method builds the acutal Enterprise Message. (e.g. - Person/BasicPerson-Update-Request or Person/BasicPerson-Update-Sync).
* This Enterprise Message will then be sent via JMS to a queue/topic and consumed
* by a Gateway which will process the request/sync and return a reply if appropriate. Note,
* replies will only be returned when a request message is sent.
*
*<P>
* @param updateDoc Document the 'primed' xml document for this action. (e.g. - a sample BasicPerson-Create-Request.xml document).
*<P>
* @return Document the Enterprise Message with a DataArea consisting of the current contents of this object and a ControlArea
* consisting of appropriate ControlArea information based on the configuration of this object. (e.g. - Sender, Datetime etc.)
*<P>
* @throws EnterpriseObjectUpdateException if errors serializing the object as an Xml Element.
* @throws JDOMException if errors occur manipulating the document.
*/
protected void buildUpdateMessage(Document updateDoc)
throws JDOMException, EnterpriseObjectUpdateException {
if (updateDoc == null) {
throw new EnterpriseObjectUpdateException("No primed Update document. Can't continue.");
}
logger.debug("buildUpdateMessage, updateDoc: " + updateDoc.toString());
// Set the elements/attributes of the doc for a update
// Control area
Element controlArea = getControlArea(updateDoc.getRootElement());
// Data area
Element dataArea = updateDoc.getRootElement().getChild("DataArea");
Element oldElement = dataArea.getChild("NewData").getChild(getElementName());
Element oldBaselineElement = dataArea.getChild("BaselineData").getChild(getElementName());
XMLOutputter xmlOut = new XMLOutputter();
Element newElement = null;
try {
newElement = (Element)buildOutputFromObject();
}
catch (EnterpriseLayoutException e) {
logger.fatal("Error building updateMessage for object " + getClass().getName() + " content of object: " + toString());
String errMessage = "Error building updateMessage for object " +
getClass().getName() + " Exception: " + e.getMessage();
throw new EnterpriseObjectUpdateException(errMessage, e);
}
// need check for null baseline!
if (getBaseline() == null) {
logger.warn("buildUpdateMessage: baseline is null!");
try {
setBaseline((XmlEnterpriseObject)clone());
getBaseline().buildObjectFromInput(oldBaselineElement);
}
catch (Exception e) {
logger.fatal("Error building baseline object from element for object (baseline/clone) " +
getBaseline().getClass().getName() + " content of element: " +
xmlOut.outputString(oldBaselineElement));
String errMessage = "Error building baseline object from element for object " +
getClass().getName() + " Exception: " + e.getMessage();
throw new EnterpriseObjectUpdateException(errMessage, e);
}
}
else {
logger.debug("buildUpdateMessage: baseline was not null...");
}
Element newBaselineElement = null;
try {
newBaselineElement = (Element)getBaseline().buildOutputFromObject();
}
catch (EnterpriseLayoutException e) {
logger.fatal("Error building updateMessage for object " + getBaseline().getClass().getName() + " content of object: " + getBaseline().toString());
logger.fatal(e.getMessage(), e);
String errMessage = "Error building updateMessage for object " +
getClass().getName() + " Exception: " + e.getMessage();
throw new EnterpriseObjectUpdateException(errMessage, e);
}
dataArea.getChild("NewData").removeChild(getElementName());
dataArea.getChild("NewData").addContent(newElement);
dataArea.getChild("BaselineData").removeChild(getElementName());
dataArea.getChild("BaselineData").addContent(newBaselineElement);
// Add the Sender, Datetime and ExpectedReplyFormat to the ControlArea
try {
controlArea = setControlArea(controlArea);
}
catch (EnterpriseObjectException e) {
String errMessage = "[JmsEnterpriseObject] Exception setting control area of Update document. Exception: " + e.getMessage();
logger.fatal(errMessage);
throw new EnterpriseObjectUpdateException(errMessage, e);
}
}
/**
* Delete message production. Builds an XML document (using the primed delete document
* as a "template") out of the current contents of the object. While building the
* XML Delete document, it will validate contents of the object checking
* for any malformed, missing or invalid fields. Field data values are also
* checked for validitity against the EnterpriseObjects document. Uses the
* producer passed in to send the XML document as a JMS request to the queue
* connected to by the producer which then processes the response checking
* for any errors in the response.
*<P>
* @param deleteAction String the delete action ('delete' or 'purge')
*<P>
* @param producer PointToPointProducer a pre-configured and started PointToPointProducer
* which will be used to send the delete-request message to the appropriate destination
* and return the reply to this method (via the PointToPointProducer.produceRequest method).
*<P>
* @return XmlEnterpriseObject a generic response
* which will indicate success or failure. If failure, any error information will
* be included in that result. The object returned is actually a
* org.openeai.moa.objects.Result object.
*<P>
* @throws EnterpriseObjectDeleteException if any errors occur when validating
* the contents of the object, if any errors occur while producing the request
* or if the object doesn't support the create action. This exception will also
* be thrown if the contents of the reply document contains an error. That is, if
* the consuming application had errors processing the request. This exception will
* include the Result information containing the error that occurred.
*/
public synchronized final XmlEnterpriseObject delete(String deleteAction, RequestService producer)
throws EnterpriseObjectDeleteException {
try {
setSenderInfo("Delete", producer);
Document doc = getDeleteDoc();
buildDeleteMessage(deleteAction, doc);
return processRequest("Delete", producer, doc);
}
catch (Exception e) {
String errMessage = "Error processing the delete request for object " +
getClass().getName() + " Exception: " + e.getMessage();
throw new EnterpriseObjectDeleteException(errMessage, e);
}
}
/**
* Delete Sync message production. Builds an XML document (using the primed
* delete sync document as a baseline) out of the current contents of the object.
* While building the XML Delete Sync document, it will validate contents of
* the object checking for any malformed, missing or invalid fields.
* Field data values are also checked for validitity against the
* EnterpriseObjects document. Publishes the XML document in a JMS message
* to the topic connected to by producer.
*<P>
* @param deleteAction String delete action ('purge' or 'delete')
*<P>
* @param producer PubSubProducer a pre-configured and started PubSubProducer
* which will be used to send the delete-sync message to the appropriate destination
* (via the PubSubProducer.publishMessage method).
*<P>
* @throws EnterpriseObjectSyncException if any errors occur when validating
* the contents of the object. Or, if any errors occur while publishing the message.
*/
public synchronized final void deleteSync(String deleteAction, SyncService producer)
throws EnterpriseObjectSyncException {
try {
setSenderInfo("DeleteSync", producer);
Document doc = getDeleteSyncDoc();
buildDeleteMessage(deleteAction, doc);
processSync("DeleteSync", producer, doc);
}
catch (Exception e) {
String errMessage = "Error producing the deleteSync message for object " +
getClass().getName() + " Exception: " + e.getMessage();
throw new EnterpriseObjectSyncException(errMessage, e);
}
}
/**
* Using the current contents of the object along with the 'primed' XML document
* this method builds the acutal Enterprise Message. (e.g. - Person/BasicPerson-Delete-Request or Person/BasicPerson-Delete-Sync).
* This Enterprise Message will then be sent via JMS to a queue/topic and consumed
* by a Gateway which will process the request/sync and return a reply if appropriate. Note,
* replies will only be returned when a request message is sent.
*
*<P>
* @param deleteDoc Document the 'primed' xml document for this action. (e.g. - a sample BasicPerson-Create-Request.xml document).
*<P>
* @return Document the Enterprise Message with a DataArea consisting of the current contents of this object and a ControlArea
* consisting of appropriate ControlArea information based on the configuration of this object. (e.g. - Sender, Datetime etc.)
*<P>
* @throws EnterpriseObjectDeleteException if errors serializing the object as an Xml Element.
* @throws JDOMException if errors occur manipulating the document.
*/
protected void buildDeleteMessage(String deleteAction, Document deleteDoc)
throws JDOMException, EnterpriseObjectDeleteException {
if (deleteDoc == null) {
throw new EnterpriseObjectDeleteException("No primed Delete document. Can't continue.");
}
logger.debug("buildDeleteMessage, deleteDoc: " + deleteDoc.toString());
// Set the elements/attributes of the doc for a Delete
// Control area
Element controlArea = getControlArea(deleteDoc.getRootElement());
// Data area
Element dataArea = deleteDoc.getRootElement().getChild("DataArea");
Element oldElement = dataArea.getChild("DeleteData").getChild(getElementName());
dataArea.getChild("DeleteData").getChild("DeleteAction").getAttribute("type").setValue(deleteAction);
Element newElement = null;
try {
newElement = (Element)buildOutputFromObject();
}
catch (EnterpriseLayoutException e) {
logger.fatal("Error building deleteMessage for object " + getClass().getName() + " content of object: " + toString());
String errMessage = "Error building deleteMessage for object " +
getClass().getName() + " Exception: " + e.getMessage();
throw new EnterpriseObjectDeleteException(errMessage, e);
}
dataArea.getChild("DeleteData").removeChild(getElementName());
dataArea.getChild("DeleteData").addContent(newElement);
// Add the Sender, Datetime and ExpectedReplyFormat to the ControlArea
try {
controlArea = setControlArea(controlArea);
}
catch (EnterpriseObjectException e) {
String errMessage = "[JmsEnterpriseObject] Exception setting control area of Delete document. Exception: " + e.getMessage();
logger.fatal(errMessage);
throw new EnterpriseObjectDeleteException(errMessage, e);
}
}
/**
* Query message production. Builds an XML document (using the primed query document
* as a "template") out of the current contents of the keyObject passed in.
* While building the XML Query message, it will validate contents of the keyObject checking
* for any malformed, missing or invalid fields. Field data values are also
* checked for validitity against the EnterpriseObjects document. Uses the
* producer passed in to send the XML document as a JMS request to the queue
* connected to by the producer which then processes the response checking
* for any errors in the response.
*<P>
* @param keyObject XmlEnterpriseObject to use as retreival arguments in the Query.
* The contents of this object are inserted into the Query document prior to
* sending the query request. Under normal conditions, this object is
* actually a LightweightPerson object since that's the most common query object used.
*<P>
* NOTE: This method should be over-ridden by lower level classes if the keyObject
* isn't a LightweightPerson.
*<P>
* @param producer PointToPointProducer a pre-configured and started PointToPointProducer
* which will be used to send the update-request message to the appropriate destination
* and return the reply to this method (via the PointToPointProducer.produceRequest method).
*<P>
* @return java.util.List this is a list of XmlEnterpriseObject objects
* (like BasicPerson, BasicEmployee etc.) provided by the authoritative source
* (the application consuming the query request).
*<P>
* @throws EnterpriseObjectQueryException if any errors occur when validating
* the contents of the object, if any errors occur while producing the request
* or if the object doesn't support the create action. This exception will also
* be thrown if the contents of the reply document contains an error. That is, if
* the consuming application had errors processing the request. This exception will
* include the Result information containing the error that occurred.
*/
public final synchronized java.util.List query(XmlEnterpriseObject keyObject, RequestService producer)
throws EnterpriseObjectQueryException {
// Since every object performing these actions are going to be using an XML
// Layout Manager for querying, updating, deleting ect. we need to save the
// layout manager the object is currently using, then change the manager
// to be the XML layout manager (which all objects should have by default)
// After all of this is done, we'll set the layout manager back to what it
// was when this method was called...
saveLayoutManagers();
java.util.List retList = new ArrayList();
Document queryDoc = null;
try {
setSenderInfo("Query", producer);
queryDoc = getQueryDoc();
buildQueryMessage(queryDoc, keyObject);
if (getValidation()) {
XmlValidator xmlValidator = new XmlValidator();
if (xmlValidator.isValid(queryDoc) == false) {
outputXMLDocument(queryDoc, "Contents of Query document.");
throw new EnterpriseObjectQueryException(getElementName() +
" - Query document is not valid!");
}
}
try {
Document responseDoc = producer.produceRequest(this, queryDoc);
Element controlArea = getControlArea(responseDoc.getRootElement());
logger.debug("ControlArea is " + controlArea.getName());
// Check for errors. If the ControlArea contains a Generic-Response-Reply we
// have an error situation. We'll need to instantiate a Result object instead
// of assuming we've got valid results from the query.
Element eResult = controlArea.getChild("Result");
if (eResult == null) {
logger.fatal("Couldn't find a result element in the returned response.");
outputXMLDocument(responseDoc, "Contents of Query Response document.");
}
// Check for failure in the provide-reply. If an error occurred, throw an exception.
Result aResult = new Result();
try {
aResult.setInputLayoutManager(getInputLayoutManager("xml"));
aResult.setOutputLayoutManager(getOutputLayoutManager("xml"));
aResult.buildObjectFromInput(eResult);
if (aResult.getStatus().equalsIgnoreCase("failure")) {
setLastErrors(aResult.getError());
StringBuffer sBuf = new StringBuffer();
for (int i=0; i<aResult.getErrorLength(); i++) {
sBuf.append("Error [" + i + "] Number: " + aResult.getError(i).getErrorNumber() + "\n");
sBuf.append("Error [" + i + "] Description: " + aResult.getError(i).getErrorDescription() + "\n\n");
}
String errMessage = "An error occurred processing the Query request\n\n" + new String(sBuf);
logger.fatal(errMessage);
throw new EnterpriseObjectQueryException(errMessage);
}
}
catch (EnterpriseLayoutException e) {
outputXMLDocument(responseDoc, "Contents of Query Response document.");
String errMessage = "Error building the Result object from the Query Response document. " +
"Exception: " + e.getMessage();
throw new EnterpriseObjectQueryException(errMessage, e);
}
// Return the list of objects returned from the query.
Element dataArea = responseDoc.getRootElement().getChild("DataArea");
// For each object under DataArea, instantiate and popultate a Java object
// that corresponds to the messageObject in the XML with the contents of the XML...
java.util.List dataAreaChildren = dataArea.getChildren();
for (int i=0; i<dataAreaChildren.size(); i++) {
XmlEnterpriseObject anObj = (XmlEnterpriseObject)clone();
Element eChild = (Element)dataAreaChildren.get(i);
try {
anObj.setEnterpriseFields((EnterpriseFields)getEnterpriseFields().clone());
anObj.buildObjectFromInput(eChild);
}
catch (EnterpriseLayoutException e) {
logger.fatal("Error building the " + eChild.getName() +
" object from the Query response document.");
outputXMLDocument(responseDoc, "Contents of Query Response document.");
logger.fatal(e.getMessage(), e);
String errMessage = "Error building the " + eChild.getName() +
" object from the Query response document. Exception: " + e.getMessage();
throw new EnterpriseObjectQueryException(errMessage, e);
}
XmlEnterpriseObject xeoBaseline = (XmlEnterpriseObject)anObj.clone();
anObj.setBaseline(xeoBaseline);
retList.add(anObj);
}
}
catch (TransportException e) {
throw new EnterpriseObjectQueryException(getElementName() +
" - Error producing Query query request! Exception: " + e.getMessage(), e);
}
}
catch (JDOMException e) {
String errMessage = "Unknown JDOM Error occurred producing the Query request. " +
"Content of object: " + toString() + " Content of queryObject: " + keyObject.toString() +
" Exception: " + e.getMessage();
throw new EnterpriseObjectQueryException(errMessage, e);
}
catch (Exception e) {
String errMessage = "Unknown Error occurred producing the Query request. " +
"Content of object: " + toString() + " Content of queryObject: " + keyObject.toString() +
" Exception: " + e.getMessage();
throw new EnterpriseObjectQueryException(errMessage, e);
}
restoreLayoutManagers();
return retList;
}
/**
* Using the current contents of the 'keyObject' passed in along with the 'primed' XML document
* this method builds the acutal Enterprise Message. (e.g. - Person/BasicPerson-Query-Request).
* This Enterprise Message will then be sent via JMS to a queue/topic and consumed
* by a Gateway which will process the query request and return an appropriate provide-reply.
*
*<P>
* This method may be overridden by ancestors of JmsEnterpriseObject if they require a different 'keyObject' in
* the Query. (e.g. - InstituitonalIdentity requires an UnknownPerson instead of a LightweightPerson).
*<P>
* @param keyObject XmlEnterpriseObject the 'keyObject' to be used in the Query. (e.g. - LightweightPerson)
* @param queryDoc Document the 'primed' xml document for this action. (e.g. - a sample BasicPerson-Create-Request.xml document).
*<P>
* @return Document the Enterprise Message with a DataArea consisting of the current contents of the 'keyObject' and a ControlArea
* consisting of appropriate ControlArea information based on the configuration of this object. (e.g. - Sender, Datetime etc.)
*<P>
* @throws EnterpriseObjectQueryException if errors serializing the object as an Xml Element.
* @throws JDOMException if errors occur manipulating the document.
*/
protected void buildQueryMessage(Document queryDoc, XmlEnterpriseObject keyObject)
throws JDOMException, EnterpriseObjectQueryException {
if (queryDoc == null) {
throw new EnterpriseObjectQueryException("No primed Query document. Can't continue.");
}
logger.debug("buildQueryMessage, queryDoc: " + queryDoc.toString());
// Set the elements/attributes of the doc for a Query
// Control area
Element controlArea = getControlArea(queryDoc.getRootElement());
// Data area
Element dataArea = queryDoc.getRootElement().getChild("DataArea");
Element eLightweightPerson = null;
try {
String className = keyObject.getClass().getName();
String keyObjectName = className.substring(className.lastIndexOf('.') + 1);
if (keyObject.getEnterpriseFields().getFieldsForObject(keyObjectName) == null ||
keyObject.getEnterpriseFields().getFieldsForObject(keyObjectName).size() == 0) {
logger.debug("Have to use parent's EO document...");
keyObject.setInputLayoutManager(getInputLayoutManager());
keyObject.setOutputLayoutManager(getOutputLayoutManager());
keyObject.setEnterpriseFields(getEnterpriseFields());
}
else {
logger.debug("using keyObject's EO document...");
}
eLightweightPerson = (Element)keyObject.buildOutputFromObject();
}
catch (Exception e) {
logger.fatal("Error building queryMessage for object " + getClass().getName() + " content of object: " + toString());
String errMessage = "Error building queryMessage for object " +
getClass().getName() + " Exception: " + e.getMessage();
throw new EnterpriseObjectQueryException(errMessage, e);
}
dataArea.removeChild("LightweightPerson");
dataArea.addContent(eLightweightPerson);
// Add the Sender, Datetime and ExpectedReplyFormat to the ControlArea
try {
controlArea = setControlArea(controlArea);
}
catch (EnterpriseObjectException e) {
String errMessage = "[JmsEnterpriseObject] Exception setting control area of Query document. Exception: " + e.getMessage();
logger.fatal(errMessage);
throw new EnterpriseObjectQueryException(errMessage, e);
}
}
/**
* Generate message production. Builds an XML document (using the primed generate document
* as a "template") out of the current contents of the object. While building the
* XML Generate document, it will validate contents of the object checking
* for any malformed, missing or invalid fields. Field data values are also
* checked for validitity against the EnterpriseObjects document. Uses the
* producer passed in to send the XML document as a JMS request to the queue
* connected to by the producer which then processes the response checking
* for any errors in the response.
*<P>
* @param producer PointToPointProducer a pre-configured and started PointToPointProducer
* which will be used to send the update-request message to the appropriate destination
* and return the reply to this method (via the PointToPointProducer.produceRequest method).
*<P>
* @param keyObject XmlEnterpriseObject to use as generation "seed" data
* (like an UnknownPerson for an InstitutionalIdentity-Generate-Request).
*<P>
* @return java.util.List this is a list of XmlEnterpriseObject objects generated by the authoritative source
* (the application consuming the request to generate). Typically, this will
* be only one object (like an InstitutionalIdentity).
*<P>
* @throws EnterpriseObjectGenerateException if any errors occur when validating
* the contents of the object, if any errors occur while producing the request
* or if the object doesn't support the create action. This exception will also
* be thrown if the contents of the reply document contains an error. That is, if
* the consuming application had errors processing the request. This exception will
* include the Result information containing the error that occurred.
*/
public synchronized final java.util.List generate(XmlEnterpriseObject keyObject, RequestService producer)
throws EnterpriseObjectGenerateException {
// Since every object performing these actions are going to be using an XML
// Layout Manager for querying, updating, deleting ect. we need to save the
// layout manager the object is currently using, then change the manager
// to be the XML layout manager (which all objects should have by default)
// After all of this is done, we'll set the layout manager back to what it
// was when this method was called...
saveLayoutManagers();
java.util.List retList = new ArrayList();
XmlEnterpriseObject anObj = null;
Document genDoc = null;
try {
setSenderInfo("Generate", producer);
genDoc = getGenerateDoc();
buildGenerateMessage(genDoc, keyObject);
if (getValidation()) {
XmlValidator xmlValidator = new XmlValidator();
if (xmlValidator.isValid(genDoc) == false) {
outputXMLDocument(genDoc, "Contents of Generate document.");
throw new EnterpriseObjectGenerateException("Generate document is not valid for " + getClass().getName());
}
}
/*
TextMessage responseMessage = (TextMessage)produceRequest(producer, genDoc);
String responseText = responseMessage.getText();
if (responseText == null) {
throw new EnterpriseObjectGenerateException("A null response was returned from the server - " + getElementName());
}
*/
// Get the results out of the TextMessage
Document responseDoc = null;
// try {
// SAXBuilder builder = new SAXBuilder(false);
// responseDoc = builder.build(new ByteArrayInputStream(responseText.getBytes()));
responseDoc = producer.produceRequest(this, genDoc);
if (getValidation()) {
XmlValidator xmlValidator = new XmlValidator();
if (xmlValidator.isValid(responseDoc) == false) {
throw new EnterpriseObjectGenerateException("Generate response document is not valid for " + getClass().getName());
}
}
Element controlArea = getControlArea(responseDoc.getRootElement());
logger.debug("ControlArea is " + controlArea.getName());
// Check for errors.
Element eResult = null;
eResult = controlArea.getChild("Result");
if (eResult == null) {
logger.fatal("Couldn't find a result element in the returned response.");
outputXMLDocument(responseDoc, "Contents of response document.");
}
// There may have been an error in the generate
Result result = new Result();
try {
result.setInputLayoutManager(getInputLayoutManager());
result.setOutputLayoutManager(getOutputLayoutManager());
result.buildObjectFromInput(eResult);
}
catch (EnterpriseLayoutException e) {
outputXMLDocument(responseDoc, "Contents of response document.");
String errMessage = "Error building the Result object from the contents of the response document. Exception: " +
e.getMessage();
throw new EnterpriseObjectGenerateException(errMessage, e);
}
// Throw an exception if there were errors in the generate
if (result.getStatus().equalsIgnoreCase("failure")) {
StringBuffer sBuf = new StringBuffer();
for (int i=0; i<result.getErrorLength(); i++) {
sBuf.append("Error [" + i + "] Number: " + result.getError(i).getErrorNumber() + "\n");
sBuf.append("Error [" + i + "] Description: " + result.getError(i).getErrorDescription() + "\n\n");
}
String errMessage = "An error occurred processing the Genrate request\n\n" + new String(sBuf);
logger.fatal(errMessage);
throw new EnterpriseObjectGenerateException(errMessage);
}
// Return the object(s) that were generated
Element dataArea = responseDoc.getRootElement().getChild("DataArea");
// For each object under DataArea, instantiate and popultate a Java object
// with the contents of the XML. We're doing it this way to remain consistent with
// the "query" method... So, we're always passing the actual element itself
// to the buildObjectFromInput method.
int numObjects = dataArea.getChildren().size();
java.util.List dataAreaChildren = dataArea.getChildren();
for (int i=0; i<numObjects; i++) {
anObj = (XmlEnterpriseObject)clone();
Element theElement = (Element)dataAreaChildren.get(i);
try {
anObj.setEnterpriseFields((EnterpriseFields)getEnterpriseFields().clone());
anObj.buildObjectFromInput(theElement);
}
catch (EnterpriseLayoutException e) {
outputXMLDocument(responseDoc, "Contents of response document.");
String errMessage = "Error building the " + theElement.getName() +
" object from the contents of the response document. Exception: " + e.getMessage();
throw new EnterpriseObjectGenerateException(errMessage, e);
}
anObj.setBaseline(anObj);
retList.add(anObj);
}
// }
/*
catch (JDOMException e) {
outputXMLDocument(responseDoc, "Contents of response document.");
String errMessage = "Unknown JDOM Exception has occurred while processing " +
"the response document. Exception: " + e.getMessage();
throw new EnterpriseObjectGenerateException(errMessage, e);
}
*/
}
catch (JDOMException e) {
outputXMLDocument(genDoc, "Contents of response document.");
String errMessage = "Unknown JDOM Exception has occurred while producing " +
"the generate request. Exception: " + e.getMessage();
throw new EnterpriseObjectGenerateException(errMessage, e);
}
catch (Exception e) {
String errMessage = "Unknown Exception has occurred while producing the generate request. " +
"Content of object: " + toString() + " Exception: " + e.getMessage();
throw new EnterpriseObjectGenerateException(errMessage, e);
}
restoreLayoutManagers();
return retList;
}
// Generate is most likely specific to the object in most cases, so it's defined
// as abstract.
protected abstract void buildGenerateMessage(Document generateDoc, XmlEnterpriseObject keyObject)
throws JDOMException, EnterpriseObjectGenerateException;
private XmlEnterpriseObject processRequest(String title, RequestService producer, Document doc)
throws EnterpriseObjectException {
// Since every object performing these actions are going to be using an XML
// Layout Manager for querying, updating, deleting ect. we need to save the
// layout manager the object is currently using, then change the manager
// to be the XML layout manager (which all objects should have by default)
// After all of this is done, we'll set the layout manager back to what it
// was when this method was called...
saveLayoutManagers();
Result aResult = null;
try {
if (getValidation()) {
XmlValidator xmlValidator = new XmlValidator();
if (xmlValidator.isValid(doc) == false) {
logger.fatal(title + " document is not valid.");
outputXMLDocument(doc, "Contents of " + title + " document.");
throw new EnterpriseObjectException(title + " document is not valid!");
}
}
Document responseDoc = producer.produceRequest(this, doc);
aResult = (Result)handleStandardResponse(responseDoc);
if (aResult.getStatus().equalsIgnoreCase("failure")) {
setLastErrors(aResult.getError());
StringBuffer sBuf = new StringBuffer();
for (int i=0; i<aResult.getErrorLength(); i++) {
sBuf.append("Error [" + i + "] Number: " + aResult.getError(i).getErrorNumber() + "\n");
sBuf.append("Error [" + i + "] Description: " + aResult.getError(i).getErrorDescription() + "\n\n");
}
String errMessage = "An error occurred processing the " + title + " request\n\n" + new String(sBuf);
logger.fatal(errMessage);
throw new EnterpriseObjectException(errMessage);
}
}
catch (Exception e) {
outputXMLDocument(doc, "Contents of " + title + " document.");
String errMessage = "Unknown exception processing " + title +
" request. Exception: " + e.getMessage();
throw new EnterpriseObjectException(errMessage, e);
}
restoreLayoutManagers();
return aResult;
}
//============================================================================
//============================================================================
// Generic method used above.
private void processSync(String title, SyncService producer, Document syncDoc)
throws EnterpriseObjectSyncException {
// Since every object performing these actions are going to be using an XML
// Layout Manager for querying, updating, deleting ect. we need to save the
// layout manager the object is currently using, then change the manager
// to be the XML layout manager (which all objects should have by default)
// After all of this is done, we'll set the layout manager back to what it
// was when this method was called...
saveLayoutManagers();
try {
if (getValidation()) {
XmlValidator xmlValidator = new XmlValidator();
if (xmlValidator.isValid(syncDoc) == false) {
throw new EnterpriseObjectSyncException(title + " document is not valid!");
}
}
producer.publishMessage(this, syncDoc);
}
catch (Exception e) {
String errMessage = "Error processing the sync message for object " +
getClass().getName() + "Content of object: " + toString() + " Exception: " + e.getMessage();
throw new EnterpriseObjectSyncException(errMessage, e);
}
restoreLayoutManagers();
}
// standard response handler
private XmlEnterpriseObject handleStandardResponse(Document responseDoc)
throws EnterpriseObjectException {
Result aResult = null;
// Get the results out of the Response document
try {
Element controlArea = getControlArea(responseDoc.getRootElement());
Element eResult = controlArea.getChild("Result");
if (eResult == null) {
String errMsg = "Couldn't find a Result element in the response returned from the server.";
throw new EnterpriseObjectException(errMsg);
}
// Instantiate a Result object passing the result Element to it
// Is it safe to just instantiate a Result object instead of using the
// class name from the message? Because the class name from the message
// is "Generic". I'm not sure this makes sense? Will the response from
// a Create, Update, Assign or Delete always include the Result only????
aResult = new Result();
try {
aResult.setInputLayoutManager(getInputLayoutManager("xml"));
aResult.setOutputLayoutManager(getOutputLayoutManager("xml"));
aResult.buildObjectFromInput(eResult);
}
catch (Exception e) {
outputXMLDocument(responseDoc, "Contents of response document.");
String errMessage = "Error building Result object out of standard response document. Exception: " + e.getMessage();
throw new EnterpriseObjectException(errMessage, e);
}
}
catch (Exception e) {
String errMessage = "Unknown Error occurred processing the standard response document. Exception: " + e.getMessage();
throw new EnterpriseObjectException(errMessage, e);
}
return aResult;
}
private void setLastErrors(java.util.List errors) {
if (errors != null) {
m_lastErrors = errors;
}
}
/**
* Returns a List containing the last errors encountered by this object during
* a Request action (create, query, generate, update, delete). When an error
* occurs during one of those actions, an exception is thrown. Therefore, the
* calling application doesn't have access to the errror objects in the result.
* This convenience method gives them access to those Error objects so they
* may use them accordingly in their application instead of having to parse
* the exception message etc.
* <P>
* @return java.util.List the list of errors that were saved the last time this
* object had an error performing a request action.
**/
public java.util.List getLastErrors() {
if (m_lastErrors == null) {
return new java.util.ArrayList();
}
else {
return m_lastErrors;
}
}
// Build the dynamic portion of the control area
protected Element setControlArea(Element controlArea) throws EnterpriseObjectException {
// If they get here, we're assuming that m_messageId and m_authentication
// have been set by the calling application.
// Save the expected reply format element because we have to add
// it back in later.
Element eReplyFormat = controlArea.getChild("ExpectedReplyFormat");
// In the case of Sync messages, there will not be an ExpectedReplyFormat
if (eReplyFormat != null) {
controlArea.removeChild("ExpectedReplyFormat");
}
// Set the sender element
controlArea.removeChild("Sender");
Sender sender = new Sender();
sender.setMessageId(getMessageId());
sender.setTestId(getTestId());
sender.setAuthentication(getAuthentication());
Element eSender = null;
try {
eSender = (Element)sender.buildOutputFromObject();
}
catch (EnterpriseLayoutException e) {
String errMessage = "[JmsEnterpriseObject] Exception building Sender Element from object. Exception: " + e.getMessage();
logger.fatal(errMessage);
throw new EnterpriseObjectException(errMessage, e);
}
// Set the datetime element
controlArea.removeChild("Datetime");
Datetime dt = new Datetime();
Element eDatetime = null;
try {
eDatetime = (Element)dt.buildOutputFromObject();
}
catch (EnterpriseLayoutException e) {
String errMessage = "[JmsEnterpriseObject] Exception building Datetime Element from object. Exception: " + e.getMessage();
logger.fatal(errMessage);
throw new EnterpriseObjectException(errMessage, e);
}
controlArea.addContent(eSender);
controlArea.addContent(eDatetime);
// In the case of Sync messages, there will not be an ExpectedReplyFormat
if (eReplyFormat != null) {
controlArea.addContent(eReplyFormat);
}
return controlArea;
}
/**
* Object initialization. This method initializes the current object with information
* contained in the EnterpriseConfigurationObject passed in. All JmsEnterpriseObjects
* inherit this functionality and the init method is called when the AppConfig object
* is being built for a particular application. This method will never be called
* directly from an application. The call is "behind the scenes". The values stored in the config
* object passed in are retreived from the Application Deployment and EnterpriseObjects
* documents. This includes configuration information for things like: Primed Documents,
* Xml Validation, Enterprise field rules (translation, formats, scrubbing etc.) and
* Layout management. See the documentation for the Application Deployment and
* EnterpriseObjects Xml documents for more information on these items. More information
* can also be found in the JavaDoc for the org.openeai.config package.
*<P>
* @param mConfig MessageObjectConfig object loaded with all configuration information
* relative to this object built from the configuration document.
*<P>
* @throws EnterpriseObjectException if errors occur initializing the current MessageObject.
*/
public void init(MessageObjectConfig mConfig) throws EnterpriseObjectException {
Properties props = mConfig.getProperties();
super.init(mConfig);
try {
setCommandName(props.getProperty("CommandName",""));
MessageId msgId = new MessageId();
msgId.setSenderAppId(props.getProperty("SenderAppId",""));
msgId.setProducerId("Unknown");
msgId.setMessageSeq("Unknown");
setMessageId(msgId);
Authentication auth = new Authentication();
auth.setAuthUserId(props.getProperty("UserId"));
auth.setAuthUserSignature(props.getProperty("Signature"));
setAuthentication(auth);
setEnterpriseFields((EnterpriseFields)mConfig.getEnterpriseFields().clone());
}
catch (Exception e) {
throw new EnterpriseObjectException(e.getMessage(), e);
}
}
private void setSenderInfo(String action, RequestService producer)
throws EnterpriseObjectException {
if (getMessageId() == null) {
throw new EnterpriseObjectException(action + " - Message Id has not been set. " +
"A Message Id must be set before this method can be called - " + getElementName());
}
getMessageId().setProducerId(producer.getProducerId(null).getId());
// Phase 1 solution
getMessageId().setMessageSeq(Integer.toString(producer.incrementMessageSequence()));
if (getAuthentication() == null) {
throw new EnterpriseObjectException(action + " - Authentication has not been set. " +
"Authentication must be set before this method can be called - " + getElementName());
}
}
private void setSenderInfo(String action, SyncService producer)
throws EnterpriseObjectException {
if (getMessageId() == null) {
throw new EnterpriseObjectException(action + " - Message Id has not been set. " +
"A Message Id must be set before this method can be called - " + getElementName());
}
getMessageId().setProducerId(producer.getProducerId(null).getId());
// Phase 1 solution
getMessageId().setMessageSeq(Integer.toString(producer.incrementMessageSequence()));
if (getAuthentication() == null) {
throw new EnterpriseObjectException(action + " - Authentication has not been set. " +
"Authentication must be set before this method can be called - " + getElementName());
}
}
//============================================================================
//============================================================================
private void outputXMLDocument(Document doc, String message) {
try {
XMLOutputter fmt = new XMLOutputter(Format.getPrettyFormat());
logger.fatal(message);
logger.fatal("\n" + fmt.outputString(doc));
}
catch (Exception exc) {
logger.fatal("Error converting XML Document to String.");
logger.fatal(exc.getMessage(), exc);
}
}
//============================================================================
//============================================================================
private void outputXMLElement(Element element, String message) {
try {
XMLOutputter fmt = new XMLOutputter(Format.getPrettyFormat());
logger.fatal(message);
logger.fatal("\n" + fmt.outputString(element));
}
catch (Exception exc) {
logger.fatal("Error converting XML Element to String.");
logger.fatal(exc.getMessage(), exc);
}
}
/**
* Abstract method that must be implemented by decendants so this object will
* know what element it's dealing with when building Xml documents.
*
* @return String the element name.
*
*/
protected String getElementName() {
String className = getClass().getName();
String objectName = className.substring(className.lastIndexOf('.') + 1);
return objectName;
}
/**
* Sets the MessageId object associated with this object. This is a
* core part of all messages (in the ControlArea) and no message can be produced
* if this hasn't been set. However, this gets set when the Message Object is
* initialized via the init method and the MessageObjectConfig object. The
* information contained in the MessageId object is set based on contents
* of the MessagingEnterprise XML document which is used to initialize all
* message objects. No client application should ever need to call this method
* directly.
*<P>
* @param msgId MessageId
*
*/
public final void setMessageId(MessageId msgId) {
m_messageId = msgId;
}
/**
* Returns the MessageId object associated with this object. This is a
* core part of all messages (in the ControlArea) and no message can be produced
* if this hasn't been set. However, this gets set when the Message Object is
* initialized via the init method and the MessageObjectConfig object. The
* information contained in the MessageId object is set based on contents
* of the MessagingEnterprise XML document which is used to initialize all
* message objects. No client application should ever need to call this method
* directly.
*<P>
* @return MessageId the MessageId object associated with this object.
*
*/
public final MessageId getMessageId() {
return m_messageId;
}
/**
* Sets the Authentication object associated with this object. This is a
* core part of all messages (in the ControlArea) and no message can be produced
* if this hasn't been set. However, this gets set when the Message Object is
* initialized via the init method and the MessageObjectConfig object. The
* information contained in the Authentication object is set based on contents
* of the MessagingEnterprise XML document which is used to initialize all
* message objects. No client application should ever need to call this method
* directly.
*<P>
* @param auth Authentication
*
*/
public final void setAuthentication(Authentication auth) {
m_authentication = auth;
}
/**
* Returns the Authentication object associated with this object. This is a
* core part of all messages (in the ControlArea) and no message can be produced
* if this hasn't been set. However, this gets set when the Message Object is
* initialized via the init method and the MessageObjectConfig object. The
* information contained in the Authentication object is set based on contents
* of the MessagingEnterprise XML document which is used to initialize all
* message objects. No client application should ever need to call this method
* directly.
*<P>
* @return Authentication the authentication object associated with this object.
*
*/
public final Authentication getAuthentication() {
return m_authentication;
}
/**
* Sets the Command name associated with this object. This is a
* core part of all messages (a property on the JMS message) and no message can be produced
* if this hasn't been set. However, this gets set when the Message Object is
* initialized via the init method and the MessageObjectConfig object. The Command Name
* is set based on contents of the application Deployment configuration document which is used to initialize all
* message objects. No client application should ever need to call this method
* directly. This is information that will be used by a consumer of the message
* to determine which "command" to execute when it receives the message. For more information
* on Commands and the University JMS Consumer framework refer to the JavaDoc for the
* org.openeai.jms.consumer package.
*<P>
* @param name String the name of the command as specified in the configuration document.
*/
public final void setCommandName(String name) {
if (name == null) {
m_commandName = "";
}
else {
m_commandName = name;
}
}
/**
* Returns the Command name associated with this object. This is a
* core part of all messages (a property on the JMS message) and no message can be produced
* if either this hasn't been set. However, this gets set when the Message Object is
* initialized via the init method and the MessageObjectConfig object. The Command Name
* is set based on contents of the MessagingEnterprise XML document which is used to initialize all
* message objects. No client application should ever need to call this method
* directly. This is information that will be used by a consumer of the message
* to determine which "command" to execute when it receives the message. For more information
* on Commands and the University JMS Consumer framework refer to the JavaDoc for the
* org.openeai.jms.consumer package.
*<P>
* @return String message name associated with this object.
*
*/
public final String getCommandName() {
return m_commandName;
}
}