/*******************************************************************************
$Source: /cvs/repositories/openii3/project/java/source/org/openeai/config/ProducerConfig.java,v $
$Revision: 1.14 $
*******************************************************************************/
/**********************************************************************
This file is part of the OpenEAI Application Foundation or
OpenEAI Message Object API created by Tod Jackson
(tod@openeai.org) and Steve Wheat (steve@openeai.org) at
the University of Illinois Urbana-Champaign.
Copyright (C) 2002 The OpenEAI Software Foundation
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
For specific licensing details and examples of how this software
can be used to build commercial integration software or to implement
integrations for your enterprise, visit http://www.OpenEai.org/licensing.
*/
package org.openeai.config;
import org.jdom.Attribute;
import org.jdom.Element;
import org.openeai.xml.*;
import org.openeai.*;
import org.openeai.jms.producer.PubSubProducer;
/**
* A ProducerConfig is a wrapper class that takes information stored in an
* OpenEAI Deployment document (ProducerConfig Element) and stores it in a Java object.
* Then the configuration object is passed to the constructor of the OpenEAI
* MessageProducers (PointToPointProducer and PubSubProducer)
* and they are able to configure themselves with the information found in the
* config object.
* <P>
* Both PubSub and PointToPoint Producers use the same configuration information
* to initialize themselves. However, some items listed in a ProducerConfig Element
* are only used by PubSubProducers and some are only used by PointToPointProducers.
* Where this is the case, it is noted.
* <P>
* <B>Configuration Parameters:</B>
* <P>
* These are the configuration parameters specified by the ProducerConfig
* Element in the Deployment document. NOTE: Like all other OpenEAI configuration
* objects, there is a "container" level associated to ProducerConfig objects.
* Many Elements and attributes are required at that level and may be optionally
* overridden at this level. This is to avoid having to enter redundant information
* in the Deployment document if all (or most) Producers being configured should use
* the same configuration information. Therefore, many of the Producer configuration
* parameters are optional at this level but required at the "container" level. Where
* this is the case, it will be indicated by an "*".
* <P>
* Many of the parameters specified for a producer are simply JMS parameters required
* by the JMS Specification.
* <P>
* <TABLE BORDER=2 CELLPADDING=5 CELLSPACING=2>
* <TR>
* <TH>Name</TH>
* <TH>Required</TH>
* <TH>Description</TH>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>name</TD>
* <TD>yes</TD>
* <TD>Name of the Producer. This will be used in many logged messages and is
* the name by which a producer is stored in AppConfig. A Producer name should
* be unique within an application. If the 'numberOfProducers' attribute is specified
* this name will be the name of the ProducerPool object which is stored in AppConfig
* and is a collection of initialized producer objects.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>numberOfProducers</TD>
* <TD>no (default is 1)</TD>
* <TD>This parameter indicates how many producers of this type should be initialized
* with the configuration information listed and stored in a ProducerPool object. The
* ProducerPool is then stored in AppConfig and can be retrieved just like any other object
* housed by AppConfig. If the numberOfProducers attribute is specified, a ProducerPool
* will be created with that number of producers in it. If numberOfProducers is not specified
* only the Producer being configured will be stored in AppConfig.
* <P>
* It is recommended that most applications utilize a ProducerPool even if the
* number of producers in the pool is only 1. This gives the application
* flexibility to increase this number at a later time
* and potentially improve the application's performance by increasing the number of
* producers in the pool. This would be determined after a period of time depending
* on how much load the application is under on a regular basis. By increasing this
* number, performance is generally improved in a multi-threaded application because
* of Thread synchronization that must occur when only one producer is used across threads.
* Generally, this is only used for PointToPoint producers but it can be used for
* PubSubProducers as well. Generally, PointToPoint production is most critical
* when it comes to performance.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>tempPoolSize</TD>
* <TD>no (default is 5)</TD>
* <TD>This parameter indicates how many TemporaryQueues and QueueReceivers on
* those TemporaryQueues will be initialized when the Producer is first started.
* During message production, TempoaryQueues and QueueRecievers will be dynamically
* created when necessary. This is another performance enhancement that reduces
* the overhead associated to PointToPoint message production and response consumption.
* The OpenEAI PointToPoint producers use TemporaryQueues to send messages. However,
* since the producers must be thread safe, they cannot use the same temporary queue
* when a message is produced. If they did, producers being used by two threads
* concurrently, would potentially step on each other when a response was returned
* by the consumer of the request produced.
* <P>
* There are two ways to avoid this situation:
* One is to create a new TemporaryQueue and QueueReciever each time the producer produces
* a Request. This is expensive. The second alternative is to create a "pool" of
* TempoaryQueues and QueueReceivers upon initialization and restrict access to those
* resources when messages are produced and only create a new TemporaryQueue/QueueReceiver
* if an available resource cannot be obtained from the pool. Any TempoaryQueue/QueueReceiver
* that must be created because the pool is busy, is cleaned up when the request
* has been completed. By monitoring how many TempoaryQueues are being created dynamically
* for an application and setting this number higher, one can affect the
* performance of Point-To-Point messaging significantly
* because fewer resources have to be allocated each time a request is produced.
* <P>
* This is only relevant to PointToPointProducers.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>startOnInitialization</TD>
* <TD>no*</TD>
* <TD>This parameter tells the Producer whether or not it should start
* (be ready to produce/publish messages) upon initialization. If the flag is
* set to true, the Producer will initialize itself and then actually create
* the connection to the broker. If this flag is false, the Producer will
* initialize itself so that it is ready to be started but it won't actually
* create the connection to the broker. Therefore, before a producer that
* hasn't been started can actually produce/publish messages, it must be
* started explicitly by the application using it.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>acknowledgementMode</TD>
* <TD>no</TD>
* <TD>JMS parameter </TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>transacted</TD>
* <TD>no</TD>
* <TD>JMS parameter </TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>deliveryMode</TD>
* <TD>no</TD>
* <TD>JMS parameter </TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>LoggingProducer</TD>
* <TD>no</TD>
* <TD>This is a PubSubProducer object configured to publish PubSubMessages
* to a "logging" destination as they are published by the PubSubProducer being
* configured. Therefore, when a PubSubProducer publishes a message via the
* publishMessage method, that message will potentially be published to
* the logging destination by the LoggingProducer as well. This is a feature
* that allows an organization to specifically track every PubSub message
* published within their organization.
* <P>
* Because PubSub messages are not used for "synchronous" messaging as
* specified by the OpenEAI Message Protocol
* they are typically published and processed by messaging applications and gateways
* when no one is watching. Therefore, it is useful to be able to keep track of
* all messages that are published incase an emergency occurs where a message
* needs to be resubmitted etc. OpenEAI provides one logging gateway
* as a reference implementation to demonstrate the usefulness of this.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>ConnectionFactoryName</TD>
* <TD>no*</TD>
* <TD>The name of the JMS Administered object (TopicConnectionFactory or
* QueueConnectionFactory) that this Producer will use
* to initialize itself from a JMS perspective. To maintain broker independance,
* this adminstered object should be stored in a Directory server.</TD>
* </TR>
* <TD>DestinationName</TD>
* <TD>yes</TD>
* <TD>The name of the JMS Administered object (Topic or Queue) that this Producer
* will produce/publish messages to. To maintain broker independance,
* this administered object should be stored in a Directory server.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>InitialContextFactory</TD>
* <TD>no*</TD>
* <TD>The name of the JNDI context factory that will be used to retrieve the JMS
* administered objects from a Directory Server or wherever the administered
* objects are stored.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>ProviderURL</TD>
* <TD>no*</TD>
* <TD>Location where the administered objects can be retrieved from.
* To maintain broker independance, this should reference a location in a
* Directory server.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>SecurityPrincipal</TD>
* <TD>no*</TD>
* <TD>The user with which to create the InitialContext when retrieving the
* Administered objects.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>SecurityCredentials</TD>
* <TD>no*</TD>
* <TD>The credentials associated to the user with which to create the
* InitialContext when retrieving the Administered objects.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>Username</TD>
* <TD>no*</TD>
* <TD>Broker user with which to connect to the broker as. Generally, this is
* stored within the ConnectionFactory administered object so it is not needed
* here. However, if it isn't stored or you wish to override what's stored
* in the AO, you can specify that here.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>Password</TD>
* <TD>no*</TD>
* <TD>Password associated to the broker user with which to connect to the broker
* as. Generally, this is stored within the ConnectionFactory administered
* object so it is not needed here. However, if it isn't stored or you wish
* to override what's stored in the AO, you can specify that here.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>ProducerIdURL</TD>
* <TD>no*</TD>
* <TD>URL used to generate a unique Producer Id for this producer. Generally,
* this is a URL that resolves to the OpenEAI UuidGen servlet which generates
* a unique UUID and returns it to the caller. If this URL is "localhost"
* the id will be generated locally and will be unique for the machine running
* the application. This ProducerId will become part of all messages using
* the OpenEAI foundation that are produced with this producer.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>ConfigClass</TD>
* <TD>no*</TD>
* <TD>Name of the configuration class that wraps the config Element (this class)</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>ObjectClass</TD>
* <TD>yes</TD>
* <TD>Name of the Java object that will be instantiated with this Config class
* (PointToPointProducer or PubSubProducer)</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>DefaultCommandName</TD>
* <TD>no</TD>
* <TD>Used by the LoggingProducer when it publishes messages to set the COMMAND_NAME
* JMS Property on the Message that it publises to the "logging" destination. Generally, this is
* not needed because the EnterpriseLoggingService should generally execute only one
* Command (absolute command).</TD>
* </TR>
* </TABLE>
* @author Tod Jackson (tod@openeai.org)
* @author Steve Wheat (steve@openeai.org)
* @version 3.0 - 28 January 2003
* @see org.openeai.jms.producer.PointToPointProducer
* @see org.openeai.jms.producer.PubSubProducer
* @see org.openeai.jms.producer.ProducerPool
* @see org.openeai.jms.producer.TempQueuePool
* @see ConsumerConfig
*/
public class ProducerConfig
extends EnterpriseConfigurationObjectImpl
implements EnterpriseConfigurationObject {
private PubSubProducer m_loggingProducer = null;
/**
* This is the constructor used by AppConfig to instantiate the config object.
* Then, AppConfig calls this object's init(Element) method passing the configuration
* element it retrieved from the XML configuration document which this object uses
* to configure itself. After this object has initialized itself,
* it will be used to instantiate and initialize the framework object
* (MessageObject, Producers, Consumers etc.)
* with the properties it's been initialized with.
*/
public ProducerConfig() {
setType("ProducerConfig");
}
public ProducerConfig(String configDocUrl, String producerName) throws EnterpriseConfigurationObjectException {
setType("ProducerConfig");
XmlDocumentReader xmlReader = new XmlDocumentReader();
try {
setConfigDoc(xmlReader.initializeDocument(configDocUrl, getValidation()));
}
catch (XmlDocumentReaderException e) {
logger.fatal(e.getMessage(), e);
}
setName(producerName);
init();
}
public ProducerConfig(Element configElement) throws EnterpriseConfigurationObjectException {
setType("ProducerConfig");
init(configElement);
}
/**
* This method sets the logging producer that will be associated to an OpenEAI PubSubProducer.
*<P>
* All OpenEAI PubSubProducers have a LoggingProducer associated to them. This is just another
* PubSubProducer that is configured to publish messages to a "logging topic". This event
* occurs when a message is published by the publisher if the logging producer exists and is started.
* So, the end result is that messages published by PubSubProducers may get published twice. Once to the
* intended destination as specified in the PubSubProducer's configuration and once to the destination
* associated to the PubSubProducer's LoggingProducer. These "logged" messages serve as a tracking
* mechanism so that every message published by a message aware application can be persisted in a
* repository.
*<P>
* @param producer PubSubProducer the configured and potentially started "logging producer"
* @see org.openeai.jms.producer.PubSubProducer
*/
public void setLoggingProducer(PubSubProducer producer) {
m_loggingProducer = producer;
}
/**
* This method returns the logging producer that will be associated to an OpenEAI PubSubProducer.
* @return PubSubProducer
* @see PubSubProducer
*/
public PubSubProducer getLoggingProducer() {
return m_loggingProducer;
}
/**
* Implements the init(Element) method that all EnterpriseConfiguration objects must implement.
* This init method takes the Configuration element passed in and pulls out configuration information
* specific to the Producer (PubSub or PointToPoint) being initialized.
* Then it sets various instance variables and properties on itself which will
* be used by the Producer when AppConfig instantiates it passing this configuration object.
* The Producer will then use the configuration java object to initialize itself.
*
* @param configElement Element the configuration element that AppConfig has pulled from the configuration document
* relevant to the Producer being configured. Or, the element that was found in the init() method.
* @throws EnterpriseConfigurationObjectException if errors occur processing the configuration Element.
*/
public void init(Element configElement) throws EnterpriseConfigurationObjectException {
try {
String producerName = configElement.getAttribute("name").getValue();
logger.debug("Element returned: ");
logger.debug(" - " + configElement.getName());
logger.debug(" Producer Name: " + producerName);
String start = configElement.getAttribute("startOnInitialization").getValue();
// Add all attributes as 'Properties'
java.util.List attrs = configElement.getAttributes();
for (int i=0; i<attrs.size(); i++) {
Attribute attr = (Attribute)attrs.get(i);
String key = attr.getName();
String value = attr.getValue();
logger.debug("ProducerConfig, Adding " + key + " - " + value);
addProperty(key, value);
}
// Add all elements as 'Properties'
java.util.List props = configElement.getChildren();
for (int i=0; i<props.size(); i++) {
Element aProp = (Element)props.get(i);
if (aProp.getName().equals("LoggingProducer")) {
try {
setLoggingProducer(new PubSubProducer(new ProducerConfig(aProp)));
}
catch (Exception e) {
logger.fatal(e.getMessage(), e);
}
}
else {
String key = aProp.getName();
String value = aProp.getText();
logger.debug("Adding " + key + " - " + value);
addProperty(key, value);
}
}
}
catch (Exception e) {
String errMessage = "Exception configuring ProducerConfig. Exception: " + e.getMessage();
logger.fatal(errMessage, e);
throw new EnterpriseConfigurationObjectException(errMessage, e);
}
}
/**
* Implements the init() method that all EnterpriseConfiguration objects must implement.
* This init method retreives the root element of the confuration document and
* then finds the specific configuration element associated to the Producer being configured
* then it calls the init(Element) method which actually initializes the ProducerConfig
* with the information found in the configuration element.
*
*/
private void init() throws EnterpriseConfigurationObjectException {
Element rootElement = getConfigDoc().getRootElement();
logger.debug("RootElement is: " + rootElement.getName());
logger.debug("Looking for ProducerConfig named: " + getName());
// Find the element specified by producerName in the document
Element configElement = getConfigElementByAttributeValue(getName(), "name");
init(configElement);
}
}