/*******************************************************************************
$Source: /cvs/repositories/openii3/project/java/source/org/openeai/config/AppConfig.java,v $
$Revision: 1.35 $
*******************************************************************************/
/**********************************************************************
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 java.io.*;
import java.lang.reflect.*;
import java.util.*;
import javax.naming.*;
import javax.naming.directory.*;
import com.sun.jndi.ldap.*;
import org.jdom.Document;
import org.jdom.Attribute;
import org.jdom.Element;
import org.openeai.*;
import org.openeai.threadpool.*;
import org.openeai.xml.*;
import org.openeai.jms.producer.*;
import org.openeai.jms.consumer.*;
import org.openeai.dbpool.EnterpriseConnectionPool;
import org.openeai.moa.*;
/**
* The AppConfig class acts as a container for all pre-configured object that an
* application may use. It reads an application's config XML document, instantiates
* the objects contained in that document and configures them according to the
* information found in the document. Then, those objects are available to the application
* via the getObject, getObjectByType or getObjectsLike methods.
* <P>
* The config document can be stored in several different places which include: file system,
* web server or directory server. Typically, an application instantiates app config
* using a properties file that contains all the properties AppConfig needs to find
* the configuration document and configure the application listed in the properties file.
*<P>
* Currently, an AppConfig is associated to these distinct types of applications:
* <ul>
* <li>A runnable class like MessageConsumerClient or GenericAppRunner
* <li>A Servlet
* <li>A ScheduledCommand implementation
* <li>A Command implementation
* </ul>
*
* <P>
* <B>Configuration Parameters:</B>
* <P>
* These are the configuration parameters that may exist in an application's
* property file.
* <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>instanceName</TD>
* <TD>no, unless a gateway is being started</TD>
* <TD>For gateways, this property is used by PubSubConsumers to establish
* their durable subscriptions. This should be reflective of where the
* gateway is physically running (machine name etc.)</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>providerUrl</TD>
* <TD>yes</TD>
* <TD>Indicates where the config doc is located.
* If the protocol specified is "ldaps" AppConfig will use SSL to connect to the
* directory server. It does this by using JSSE. Other protocols that can be
* specified include http:, https: and file:. A local file system path can
* also be specified like "/usr/configs/ConfigDoc.xml" or "c:/user/configs/ConfigDoc.xml".</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>initialContextFactory</TD>
* <TD>no, unless the providerUrl is ldap/ldaps</TD>
* <TD>The initialContextFactory property indicates how AppConfig is to connect
* to the store (only applies to directory server connection)</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>securityPrincipal</TD>
* <TD>no, unless the providerUrl is ldap/ldaps</TD>
* <TD>The securityPrincipal and securityCredentials are directory server credentials
* used to connect to the providerUrl in the directory server. This is only related
* to configuration documents stored in a directory server.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>securityCredentials</TD>
* <TD>no, unless the providerUrl is ldap/ldaps</TD>
* <TD>The securityPrincipal and securityCredentials are directory server credentials
* used to connect to the providerUrl in the directory server. This is only related
* to configuration documents stored in a directory server.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>configDocName</TD>
* <TD>no, unless the providerUrl is ldap/ldaps</TD>
* <TD>name of the object as it's stored in the directory server.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>messageComponentName</TD>
* <TD>yes</TD>
* <TD>the application/gateway etc. being configured specifically, as it's named in the config doc.</TD>
* </TR>
* </TABLE>
* <P>
* Example properties file:
*
* <ul>
* <li>instanceName=SomeGateway-1
* <li>providerUrl=ldaps://ldap.some.company.com:636/ou=Development,ou=Configurations,ou=Messaging,dc=uillinois,dc=edu
* <li>initialContextFactory=com.sun.jndi.ldap.LdapCtxFactory
* <li>securityPrincipal=uid=Gateway1,ou=Users,ou=Messaging,dc=uillinois,dc=edu
* <li>securityCredentials=secretpw
* <li>configDocName=configxmlname=SampleGateway
* <li>messageComponentName=SampleGateway
* </ul>
*
* <P>
* Directory Server Example:
* <ul>
* <li>providerUrl=ldaps://ldap.some.company.com:636/ou=Development,ou=Configurations,ou=Messaging,dc=uillinois,dc=edu
* <li>providerUrl=ldap://ldap.some.company.com:389/ou=Development,ou=Configurations,ou=Messaging,dc=uillinois,dc=edu
*</ul>
* <P>
* Web Server Example:
* <ul>
* <li>providerUrl=http://www.aits.uillinois.edu/config/xml/SomeApp.xml
* <li>providerUrl=https://www.aits.uillinois.edu/config/xml/SomeApp.xml
* </ul>
* <P>
* File system Example:
* <ul>
* <li>providerUrl=/usr/config/xml/SomeApp.xml
* <li>providerUrl=c:/usr/config/xml/SomeApp.xml
* <li>providerUrl=file://localhost/usr/config/xml/SomeApp.xml
* </ul>
*<P>
* @author Tod Jackson (tod@openeai.org)
* @author Steve Wheat (steve@openeai.org)
* @version 3.0 - 28 January 2003
*/
public class AppConfig extends EnterpriseConfigurationObjectImpl implements EnterpriseConfigurationObject {
private Hashtable m_objects = new Hashtable(5, 0.75f);
private String m_docUri = "";
private ThreadPool m_threadPool = null;
private String m_exceptionMessage = "";
private String m_exceptionStackTrace = "";
private String m_instanceName = "";
private boolean m_exceptionOccurred = false;
private boolean m_isInitialized = false;
private String m_providerUrl = "";
private boolean m_useThreads = true;
/*
private EnterpriseFields m_fields = null;
private EnterpriseLayoutManager m_inputXmlLayoutManager = null;
private EnterpriseLayoutManager m_outputXmlLayoutManager = null;
*/
// this is the main appConfig to hold shared resources among
// commands and is set only once to be the top-level AppConfig
private static AppConfig m_mainAppConfig = null;
// private AppConfig m_mainAppConfig = null;
/**
* Constructor
*/
public AppConfig() {
setMainAppConfig(this);
setType("AppConfig");
// initializeThreadPool("10","0","1", false);
}
/**
* This constructor might be used if an application knows URI of the document that is
* to be used to configure the application. Note, this can only be a web address or a file URI.
* This is generally not needed but is provided as a potential convenience method.
*
* @throws EnterpriseConfigurationObjectException if any errors occur while initializing the component.
*/
public AppConfig(String docUri,
String appName) throws EnterpriseConfigurationObjectException {
setMainAppConfig(this);
if (isInitialized()) {
logger.info("[AppConfig] can't initialize AppConfig for " + appName +
" because it's already been initialized.");
return;
}
setType("AppConfig");
setName(appName);
setDocUri(docUri);
initializeFromFile(getDocUri(), appName);
setIsInitialized(true);
}
/**
* This is the most commonly used constructor. Most applications, gateways, servlets etc.
* will simply read a properties file on the file system which contains information regarding
* where to find the configuration document and what component to configure. See Class description for more
* details on the Properties file used.
*
* @throws EnterpriseConfigurationObjectException if any errors occur while initializing the component.
*/
public AppConfig(Properties props) throws EnterpriseConfigurationObjectException {
setMainAppConfig(this);
setProperties(props);
String appName = props.getProperty("messageComponentName");
if (appName == null) {
throw new EnterpriseConfigurationObjectException("Null 'messageComponentName' not allowed.");
}
setName(appName);
setAppName(appName);
if (isInitialized()) {
logger.info("[AppConfig] can't initialize AppConfig for " + getName() +
" because it's already been initialized.");
return;
}
String instanceName = props.getProperty("instanceName", "");
setInstanceName(instanceName);
String providerUrl = props.getProperty("providerUrl");
if (providerUrl == null) {
throw new EnterpriseConfigurationObjectException("Null 'providerUrl' not allowed.");
}
if (providerUrl.toLowerCase().indexOf("ldap") == 0) {
// initialize from a directory server
initializeFromDirectoryServer(props);
}
else {
// attempt to load config document from web server or file system
initializeFromFile(providerUrl, appName);
}
setIsInitialized(true);
}
/**
* This constructor might be used if an application has already parsed the deployment document and found
* the configuration Element associated to the application. This is generally not needed but is provided
* as a potential convenience method.
*
* @throws EnterpriseConfigurationObjectException if any errors occur while initializing the component.
*/
public AppConfig(Element configElement) throws EnterpriseConfigurationObjectException {
setMainAppConfig(this);
setType("AppConfig");
setName(configElement.getAttribute("id").getValue());
logger.info("[AppConfig] initializing application " + getName());
if (isInitialized()) {
logger.info("[AppConfig] can't initialize AppConfig for " + getName() +
" because it's already been initialized.");
return;
}
// initializeThreadPool("10","0","1", false);
init(configElement.getChild("Configuration"));
setIsInitialized(true);
}
/**
* Sets the providerUrl instance variable associated with this Application. This
* is the location in the directory server, file system or web server where the
* deployment document resides. i.e. - this is where the AppConfig object should
* "look" for the deployment document.
*
* @param url String the providerUrl.
*/
public final void setProviderUrl(String url) {
m_providerUrl = url;
}
/**
* Returns the providerUrl instance variable associated with this Application. This
* is the location in the directory server, file system or web server where the
* deployment document resides. i.e. - this is where the AppConfig object should
* "look" for the deployment document.
*
* @return String the providerUrl.
*/
public final String getProviderUrl() {
return m_providerUrl;
}
/**
* Sets the EnterpriseFields object associated with this Application.
*
*/
/*
public void setEnterpriseFields(EnterpriseFields fields) {
m_fields = fields;
}
*/
/**
* Returns the EnterpriseFields object associated with this Application.
*
* @return org.openeai.config.EnterpriseFields the
* EnterpriseFields object associated with this Application.
*
*/
/*
public EnterpriseFields getEnterpriseFields() {
return m_fields;
}
*/
/*
public void setInputXmlLayoutManager(EnterpriseLayoutManager elm) {
m_inputXmlLayoutManager = elm;
}
public EnterpriseLayoutManager getInputXmlLayoutManager() {
return m_inputXmlLayoutManager;
}
public void setOutputXmlLayoutManager(EnterpriseLayoutManager elm) {
m_outputXmlLayoutManager = elm;
}
public EnterpriseLayoutManager getOutputXmlLayoutManager() {
return m_outputXmlLayoutManager;
}
*/
/**
* Returns the main appConfig (in other words, the top-level AppConfig).
* This allows access to shared resources, such as database pools, from
* individual commands instantiated through the main or top-level AppConfig.
*/
public AppConfig getMainAppConfig() {
// wait for the main AppConfig to be fully initialized
if (m_mainAppConfig != null) {
logger.debug("Waiting for all initialization threads to complete before returning main AppConfig");
while (!m_mainAppConfig.isInitialized()) {
try {
Thread.sleep(1000);
}
catch (Exception e) {
}
}
logger.debug("done - Waiting for all initialization threads to complete before returning main AppConfig");
}
return m_mainAppConfig;
}
/**
* Sets the main appConfig to be the top-level AppConfig.
*/
private synchronized void setMainAppConfig(AppConfig appConfig) {
if (m_mainAppConfig == null)
m_mainAppConfig = appConfig;
}
/**
* Sets the AppConfig initialization indicator. When setting the indicator to true, we
* will wait until all initialization threads are complete before setting this to true.
* This is neccessary for any commands that required shared resources, that is resources
* such as database pools that may be defined only in the main or top-level AppConfig.
*/
private void setIsInitialized(boolean init) {
if (init) {
// wait for thread pool to complete its work...
if (m_threadPool != null && m_useThreads == true) {
logger.debug("Waiting for all initialization threads to complete before setting AppConfig to initialized status");
while (m_threadPool.getJobsInProgress() > 0) {
try {
Thread.sleep(1000);
}
catch (Exception e) {
}
}
}
}
m_isInitialized = init;
logger.debug("AppConfig set to " +
(m_isInitialized ? "initialized" : "uninitialized") +
" status");
}
/**
* Returns an indication that specifies if this AppConfig object has been initialized yet.
*
* @return boolean
*/
public final boolean isInitialized() {
return m_isInitialized;
}
private String getInstanceName() {
return m_instanceName;
}
private void setInstanceName(String instanceName) {
m_instanceName = instanceName;
}
/**
* Returns the config document URI that is used to locate the XML config document for
* this application.
*
* @return String
*/
public final String getDocUri() {
return m_docUri;
}
/**
* Sets the config document URI that is used to locate the XML config document for
* this application.
*
* @param docUri String
*/
public final void setDocUri(String docUri) {
m_docUri = docUri;
}
/**
* Re-initializes this AppConfig object. This method stops any running component
* that it's managing, re-reads the deployment document and re-initializes/re-starts
* all objects for this application that's listed in the deployment document.
*
* @throws EnterpriseConfigurationObjectException
*/
public final void reInitialize() throws EnterpriseConfigurationObjectException {
// Stop any running producers and/or consumers.
setIsInitialized(false);
Enumeration keys = getObjects().keys();
while (keys.hasMoreElements()) {
Object obj = getObjects().get(keys.nextElement());
if (obj instanceof PointToPointProducer) {
PointToPointProducer p2p = (PointToPointProducer)obj;
p2p.stopProducer();
}
if (obj instanceof PointToPointConsumer) {
PointToPointConsumer p2p = (PointToPointConsumer)obj;
p2p.stopConsumer();
}
if (obj instanceof PubSubProducer) {
PubSubProducer pubSub = (PubSubProducer)obj;
pubSub.stopPublisher();
}
if (obj instanceof PubSubConsumer) {
PubSubConsumer pubSub = (PubSubConsumer)obj;
pubSub.stopConsumer();
}
}
// Reset the objects hashtable to a blank Hashtable.
setObjects(new Hashtable(5, 0.75f));
// Rebuild the AppConfig object based on information used when it was initialized
// the first time.
if (getDocUri().length() > 0) {
// Initialize from file...
}
else if (getProperties() != null && getProperties().size() > 0) {
// Initialized from properties (directory server)
initializeFromDirectoryServer(getProperties());
}
else {
// error
}
}
/**
* Loads the configuration document from a URI (file or webserver etc.).
*
* @param docUri String file URI to the document (http:, file: etc.)
* @param appName String appName the name of the messaging component in the configuration document that the AppConfig object should be lookning for.
* @throws EnterpriseConfigurationObjectException
*/
public void initializeFromFile(String docUri,
String appName) throws EnterpriseConfigurationObjectException {
setType("AppConfig");
setDocUri(docUri);
try {
XmlDocumentReader xmlReader = new XmlDocumentReader();
setConfigDoc(xmlReader.initializeDocument(getDocUri(), getValidation()));
}
catch (XmlDocumentReaderException e) {
String msg =
"Error initializing document " + getDocUri() + " for Component named " +
appName + " Exception: " + e.getMessage();
logger.fatal(msg);
throw new EnterpriseConfigurationObjectException(msg, e);
}
setName(appName);
if (this.getAppName() == null || this.getAppName().length() == 0) {
setAppName(appName);
}
init();
setIsInitialized(true);
}
/**
* Loads the configuration document from a Directory Server via LDAP (ldap or ldaps)
*
* @param props Java Properties object containing the appropriate properties needed to configure this AppConfig object.
*<P>
* See class description for list of properties.
*<P>
* @throws EnterpriseConfigurationObjectException
*/
private void initializeFromDirectoryServer(Properties props) throws EnterpriseConfigurationObjectException {
setType("AppConfig");
setProperties(props);
String appName = props.getProperty("messageComponentName");
if (appName == null) {
throw new EnterpriseConfigurationObjectException("Null 'messageComponentName' not allowed.");
}
String providerUrl = props.getProperty("providerUrl");
if (providerUrl == null) {
throw new EnterpriseConfigurationObjectException("Null 'providerUrl' not allowed.");
}
String initialCtxFactory = props.getProperty("initialContextFactory");
if (initialCtxFactory == null) {
throw new EnterpriseConfigurationObjectException("Null 'initialContextFactory' not allowed.");
}
String principal = props.getProperty("securityPrincipal");
if (principal == null) {
throw new EnterpriseConfigurationObjectException("Null 'securityPrincipal' not allowed.");
}
String credentials = props.getProperty("securityCredentials");
if (credentials == null) {
throw new EnterpriseConfigurationObjectException("Null 'securityCredentials' not allowed.");
}
String configDocName = props.getProperty("configDocName");
if (configDocName == null) {
throw new EnterpriseConfigurationObjectException("Null 'configDocName' not allowed.");
}
DirContext ic = null;
Hashtable env = new Hashtable(5, 0.75f);
boolean useSsl = false;
if (providerUrl.indexOf("ldaps:") != -1) {
useSsl = true;
// Replace the ldaps with ldap
String newProviderUrl =
"ldap:" + providerUrl.substring(providerUrl.indexOf(":") + 1);
providerUrl = newProviderUrl;
logger.debug("ProviderUrl was changed to " + providerUrl);
}
logger.debug("Getting initial context");
env.put(Context.INITIAL_CONTEXT_FACTORY, initialCtxFactory);
logger.debug("Set initCtxFactory to " + initialCtxFactory);
env.put(Context.PROVIDER_URL, providerUrl);
logger.debug("Set providerUrl to " + providerUrl);
if (useSsl) {
// Bind via ssl
logger.debug("Setting SECURITY_PROTOCOL to ssl");
env.put(Context.SECURITY_PROTOCOL, "ssl");
}
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, principal);
logger.debug("Set principal to " + principal);
env.put(Context.SECURITY_CREDENTIALS, credentials);
logger.debug("Set credentials to " + credentials);
String configDocContents = null;
try {
logger.info("Creating a new InitialContext object (ie - connecting to the DirectoryServer to retrieve configuration document)...");
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
ic = new InitialDirContext(env);
logger.info("Created the InitialContext object...");
SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration results = null;
results = ic.search(providerUrl, configDocName, constraints);
if (results == null || !results.hasMoreElements()) {
throw new EnterpriseConfigurationObjectException("No matching records found for " +
configDocName);
}
else {
logger.debug("Found " + configDocName);
while (results.hasMoreElements()) {
SearchResult sr = (SearchResult)results.next();
javax.naming.directory.Attributes attrs = sr.getAttributes();
javax.naming.directory.Attribute battr =
attrs.get("configxmldocument");
configDocContents = (String)battr.get();
}
}
ic.close();
}
catch (NamingException ne) {
String errMessage =
"NamingException Creating InitialContext. Exception: " +
ne.getMessage();
logger.fatal(errMessage, ne);
throw new EnterpriseConfigurationObjectException(errMessage, ne);
}
catch (Exception e) {
String errMessage =
"UnknownException Creating InitialContext. Exception: " +
e.getMessage();
logger.fatal(errMessage, e);
throw new EnterpriseConfigurationObjectException(errMessage, e);
}
try {
XmlDocumentReader xmlReader = new XmlDocumentReader();
boolean v = getValidation();
Document cDoc =
xmlReader.initializeDocument(new StringReader(configDocContents), v);
setConfigDoc(cDoc);
}
catch (XmlDocumentReaderException e) {
String errMessage =
"Error initializing configuration document from directory server. " +
"Exception: " + e.getMessage();
logger.fatal(errMessage);
throw new EnterpriseConfigurationObjectException(errMessage, e);
}
setName(appName);
init();
setIsInitialized(true);
}
public Hashtable getObjects() {
return m_objects;
}
private void setObjects(Hashtable objects) {
m_objects = objects;
}
/**
* Returns the <b>first</b> object stored in this AppConfig that is a class of object
* matching the the class name passed in.
* <P>
* If the object has been configured with the 'refresh' attribute set to 'true'
* the object will be refreshed based on the current contents of the config document
* before it's returned. Currently, this only works for PropertyConfig objects so
* properties associated to an application like a Servlet, ScheduledApp or Gateway
* can be refreshed dynamically without restarting the app. This will only work
* if the application actually retrieves the Properties object from AppConfig
* when the business logic is executed as opposed to retrieving those properties
* when the command or servlet is first instantiated.
* <P>
* Note, XmlEnterpriseObjects stored in an AppConfig will be cloned and the clone
* will be returned. For all other types of objects stored in an AppConfig, a
* reference to that object will be returned.
*
* @param className String the class name of the object being retrieved.
* @return Object
* @throws EnterpriseConfigurationObjectException if no objects of type passed in exist.
* @see #getObject
*/
public final synchronized Object getObjectByType(String className) throws EnterpriseConfigurationObjectException {
Enumeration keys = getObjects().keys();
Object o2 = null;
Class compareClass = null;
try {
compareClass = Class.forName(className);
}
catch (ClassNotFoundException exp) {
throw new EnterpriseConfigurationObjectException(exp);
}
while (keys.hasMoreElements()) {
String name = (String)keys.nextElement();
Object o = getObjects().get(name);
logger.debug("Comparing " + className + " to " + o.getClass().getName());
if (compareClass.isInstance(o)) {
// If it's an XmlEnterpriseObject, we'll do a "deepCopy" on that object
if (o instanceof XmlEnterpriseObject) {
XmlEnterpriseObject x = (XmlEnterpriseObject)o;
try {
XmlEnterpriseObject x2 = (XmlEnterpriseObject)x.clone();
return x2;
}
catch (Exception e) {
String errMessage =
"Error performing clone on " + className + " Exception: " +
e.getMessage();
logger.fatal(errMessage);
throw new EnterpriseConfigurationObjectException(errMessage, e);
}
}
// check 'refresh' attribute associated to all EnterpriseConfiguration objects. If refresh is true,
// refresh the object before returning it.
if (o instanceof EnterpriseConfigurationObject) {
EnterpriseConfigurationObject eco = (EnterpriseConfigurationObject)o;
if (eco.refresh()) {
logger.debug("Need to refresh the " + name + " object....");
refreshObject(name);
logger.debug("Done refreshing the " + name + " object.");
return getObjects().get(name.toLowerCase());
}
}
// Otherwise, we'll just return a reference to the object.
return o;
}
}
if (o2 == null) {
String errMessage =
"AppConfig: Object of type " + className + " could not be found in this " +
getName() + " AppConfig.";
logger.debug(errMessage);
throw new EnterpriseConfigurationObjectException(errMessage);
}
return o2;
}
/**
* Returns a List of objects currently stored in this AppConfig object with names that contain
* the pattern passed in.
*
* @param pattern String the "pattern" name of the object(s) being retrieved.
* @return List of objects who's names contain the pattern passed in (empty list if none exist).
* @throws EnterpriseConfigurationObjectException if no objects exist with names containing the pattern passed in.
* @see #getObject
*/
public final synchronized java.util.List getObjectsLike(String pattern) throws EnterpriseConfigurationObjectException {
// Returns a list of objects that are stored in this AppConfig that have a name
// which contains the "pattern" passed in. For each object with that pattern
// in it's name, add it to the "returnVector" then return the vector at the end.
java.util.List returnList = Collections.synchronizedList(new ArrayList());
Set keySet = getObjects().keySet();
Iterator it = keySet.iterator();
while (it.hasNext()) {
String keyName = (String)it.next();
if (keyName.indexOf(pattern.toLowerCase().trim()) != -1) {
returnList.add(getObject(keyName));
}
}
return returnList;
}
/**
* Convenience method that returns a Properties object associated to a PropertyConfig
* object. This allows developers to retrieve named properties from an AppConfig
* without having to go through the extra step of retrieving a PropertyConfig object
* and then calling the 'getProperties' method on that object. It can still be done
* that way but this method just provides a mechanism for doing with fewer lines of
* code.
*
* @param name String the name of the PropertyConfig object being retrieved as it's named in the Deployment document.
* @return Properties object associated to the PropertyConfig object who's name matches the name passed in.
* @throws EnterpriseConfigurationException if no object exists with a name passed in or if the
* object named 'name' is not a PropertyConfig object.
* @see #getObject
*/
public final synchronized Properties getProperties(String name) throws EnterpriseConfigurationObjectException {
Object o = getObject(name);
// If it's a PropertyConfig object, get the Properties object associated
// to it and return it.
if (o instanceof PropertyConfig) {
PropertyConfig p = (PropertyConfig)o;
return p.getProperties();
}
else {
String errMessage =
"AppConfig: the object named '" + name + "' is not " +
"an instance of the PropertyConfig class.";
throw new EnterpriseConfigurationObjectException(errMessage);
}
}
/**
* Returns the object stored in this AppConfig object with the name passed in.
* The name of the objects are specified in the XML Configuration documents and
* are the names by which AppConfig stores objects as it configures itself.
* <P>
* If the object has been configured with the 'refresh' attribute set to 'true'
* the object will be refreshed based on the current contents of the config document
* before it's returned. Currently, this only works for PropertyConfig objects so
* properties associated to an application like a Servlet, ScheduledApp or Gateway
* can be refreshed dynamically without restarting the app. This will only work
* if the application actually retrieves the Properties object from AppConfig
* when the business logic is executed as opposed to retrieving those properties
* when the command or servlet is first instantiated.
* <P>
* Note, XmlEnterpriseObjects stored in an AppConfig will be cloned and the clone
* will be returned. For all other types of objects stored in an AppConfig, a
* reference to that object will be returned.
*
* @param name String the name of the object being retrieved.
* @return Object who's name matches the name passed in.
* @throws EnterpriseConfigurationException if no object exists with a name passed in.
* @see #getObjectByType
* @see #getObjectsLike
*/
public final synchronized Object getObject(String name) throws EnterpriseConfigurationObjectException {
logger.debug("Attempting to retrieve " + name + " from AppConfig");
Object o = getObjects().get(name.toLowerCase());
if (o == null) {
throw new EnterpriseConfigurationObjectException("Object named " + name +
" could not be found in this AppConfig object.");
}
logger.debug("Retrieved " + name + " from AppConfig");
try {
// If it's an XmlEnterpriseObject, we'll do a "deepCopy" on that object
if (o instanceof XmlEnterpriseObject) {
logger.debug("Performing a clone on " + name);
XmlEnterpriseObject x = (XmlEnterpriseObject)o;
try {
XmlEnterpriseObject x2 = (XmlEnterpriseObject)x.clone();
logger.debug("done cloning...");
return x2;
}
catch (Exception e) {
String errMessage =
"Error performing clone on " + x.getClass().getName() +
" Exception: " + e.getMessage();
logger.fatal(errMessage);
throw new EnterpriseConfigurationObjectException(errMessage, e);
}
}
// check 'refresh' attribute associated to all EnterpriseConfiguration objects. If refresh is true,
// refresh the object before returning it.
if (o instanceof EnterpriseConfigurationObject) {
EnterpriseConfigurationObject eco = (EnterpriseConfigurationObject)o;
if (eco.refresh()) {
// if (isInitialized()) {
logger.debug("Need to refresh the " + name + " object....");
refreshObject(name);
logger.debug("Done refreshing the " + name + " object.");
return getObjects().get(name.toLowerCase());
// }
// else {
// logger.info("Not refreshing the " + name + " object because this AppConfig is still initializing.");
// return o;
// }
}
}
// just return a reference to the object.
return o;
}
catch (Exception e) {
logger.fatal("AppConfig: Exception occurred. Exception: " +
e.getMessage());
throw new EnterpriseConfigurationObjectException("AppConfig: Exception occurred. Exception: " +
e.getMessage(), e);
}
}
private void refreshObject(String name) throws EnterpriseConfigurationObjectException {
String providerUrl = getProperties().getProperty("providerUrl");
Properties props = null;
if (providerUrl == null) {
// we must be in a command
providerUrl =
getMainAppConfig().getProperties().getProperty("providerUrl");
props = getMainAppConfig().getProperties();
}
else {
props = getProperties();
}
if (providerUrl == null) {
// the appconfig was initialized via one of the 'initializeFrom..." methods.
providerUrl = getDocUri();
setProviderUrl(providerUrl);
}
if (providerUrl.toLowerCase().indexOf("ldap") == 0) {
// refreshObjectFromDirectoryServer(name, props);
}
else {
// attempt to load config document from web server or file system
refreshObjectFromFile(name);
}
}
private void refreshObjectFromFile(String objectName) throws EnterpriseConfigurationObjectException {
Document configDoc = null;
String docUri = getDocUri();
try {
XmlDocumentReader xmlReader = new XmlDocumentReader();
if (docUri == null || docUri.length() == 0) {
// we must be in a command
docUri = getMainAppConfig().getDocUri();
}
configDoc = xmlReader.initializeDocument(docUri, false);
setConfigDoc(configDoc);
}
catch (XmlDocumentReaderException e) {
String msg =
"Error initializing document " + getDocUri() + " for Component named " +
objectName + " Exception: " + e.getMessage();
logger.fatal(msg, e);
throw new EnterpriseConfigurationObjectException(msg, e);
}
try {
String appName = this.getAppName();
logger.debug("looking for application named: " + appName);
Element mainConfigElement =
getElementByAttributeValue(getConfigDoc().getRootElement(), appName);
XmlElementLocator locator = new XmlElementLocator();
Element configElement =
locator.getElementByAttributeNameValueRecursive(mainConfigElement,
"name", objectName);
String className =
getObjects().get(objectName.toLowerCase()).getClass().getName();
setObjects(addObject(getObjects(), objectName, className, null,
configElement));
}
catch (Exception e) {
logger.fatal(e.getMessage(), e);
throw new EnterpriseConfigurationObjectException(e.getMessage(), e);
}
}
/**
* This is the "workhorse" initialization method for an AppConfig object. This method
* is called once the "Configuration" element for an application, gateway etc. has been
* retrieved from their config document. The configuration element passed in is used
* to determine which objects to instantiate and initialize with the information found
* in the Configuration element.
*<P>
* This method loops through all the child elements within the Configuration element
* passed in and instantiates configuration Java objects with those individual config elements.
* Then, it passes those Java objects to the appropriate Java object's constructor. These
* Java objects that are instantiated with the configuration objects, are then available to
* applications via the getObject, getObjectsLike and getObjectByType methods.
*<P>
* For example, an XML aware ProducerConfig object is instantiated/initialized with the ProducerConfig XML element.
* This object is then used to construct either a PointToPointProducer or a PubSubProducer foundation component
* depending on the information contained in the ProducerConfig object/element.
* Finally, the Producer that's instantiated is stored within the AppConfig object and can be retrieved
* during application execution by the application developer. This way, the Producer is instantiated, configured
* and potentially started (ready for producing messages) all based on information contained in the
* configuration document. All the application developer has to do
* is enter the appropriate configuration information into the config document and start his application. When
* the application is started, an AppConfig object is built and the appropriate Producers are added to it
* for use by the application being developed.
*
* @param eConfig Element the Configuration Element for the application, gateway, servlet etc. being configured.
* @throws EnterpriseConfigurationObjectException if any errors occur while initializing the component.
*/
public void init(Element eConfig) throws EnterpriseConfigurationObjectException {
// Now that we've found an application, we need to get the Configuration
// Element out.
java.util.List configChildren = eConfig.getChildren();
logger.debug(getName() + " has " + configChildren.size() +
" Configuration children.");
// Now we'll get a list of all config lists (MessageObjectConfigs, ProducerConfigs, etc.).
for (int i = 0; i < configChildren.size(); i++) {
Element eConfig2 = (Element)configChildren.get(i);
java.util.List configList = eConfig2.getChildren();
java.util.List defaultAttrs = eConfig2.getAttributes();
// Now, we need to go through all the Configuration elements and instantiate
// the appropriate Java Config object for that Config element. This will be based on
// the configClassName attribute in the config elements.
// MessageObjectConfigs/MessageObjectConfig, ProducerConfigs/ProducerConfig, etc.)
// This is what we'll perform utilizing the thread pool.
try {
if (m_useThreads) {
if (m_threadPool == null) {
initializeThreadPool("10", "0", "1", false);
}
m_threadPool.addJob(new InitConfigsThread(eConfig2.getName(),
defaultAttrs, configList));
}
else {
new InitConfigsThread(eConfig2.getName(), defaultAttrs,
configList).run();
}
}
catch (ThreadPoolException e) {
}
}
if (m_useThreads) {
logger.debug("Waiting for Threads to complete.");
while (m_threadPool.getJobsInProgress() > 0) {
try {
Thread.sleep(500);
}
catch (Exception e) {
}
}
logger.debug("Threads are done.");
}
if (m_exceptionOccurred) {
logger.fatal("Exception Occurrred while configuring application " +
getName());
logger.fatal("Exception Message: " + m_exceptionMessage);
logger.fatal("Exception Stack: " + m_exceptionStackTrace);
throw new EnterpriseConfigurationObjectException(m_exceptionMessage);
}
logger.debug("Added " + getObjects().size() +
" objects to object Hashtable");
logger.info("Initialization complete for Application named: " + getName());
}
/**
* Starts the initialization process for this AppConfig object.
* - retrieves the root element from the config doc
* - finds the messaging component element we're configuring (application, gateway etc.)
* - retreives the "Configuration" element from that messaging component.
* - calls the init(Element) method which performs the instantiation and initialization
* of all components listed in the Configuration element.
*<P>
* This is called when the AppConfig object is instantiated with a Properties object
* and initializes itself from a file or from a directory server.
*
* @throws EnterpriseConfigurationObjectException if any errors occur while initializing the component.
*/
private void init() throws EnterpriseConfigurationObjectException {
/*
- Go through all MessagingComponents and find this application.
- Once the app is found, loop through the Configuration element
and instantiate a ConfigObject for each configuration object listed.
- Then, take the config object and instantiate the appropriate configurable
object starting the ones that need to be started (Producers, Consumers) and
initializing the ones that need initialized (Logger).
- After they've been successfully initialized, started etc. add the names
of the objects to the m_configObjectNames Vector and add the configurable object
to the m_objects Vector.
*/
Element rootElement = getConfigDoc().getRootElement();
Element appElement = getElementByAttributeValue(rootElement, getName());
Element eConfig = null;
if (appElement != null) {
logger.debug("Found app " + getName() + " in " + appElement.getName());
eConfig = appElement.getChild("Configuration");
if (eConfig == null) {
String errMsg =
"Could not find Configuration information for application '" +
getName() +
"' check that the 'messageComponentName' property matches " +
"what's in the Deployment document.";
logger.fatal(errMsg);
throw new EnterpriseConfigurationObjectException(errMsg);
}
// determine if we should initialize this AppConfig object using threads or not.
// default is 'true' (we should use threads).
Attribute aInitializeUsingThreads =
eConfig.getAttribute("initializeUsingThreads");
if (aInitializeUsingThreads != null) {
String initUsingThreads = aInitializeUsingThreads.getValue();
logger.debug("initUsingThreads String value: '" + initUsingThreads +
"'");
if (initUsingThreads.equalsIgnoreCase("true") == false &&
initUsingThreads.equalsIgnoreCase("false") == false) {
initUsingThreads = "true";
}
m_useThreads =
new Boolean(initUsingThreads.toLowerCase()).booleanValue();
}
else {
logger.debug("initializeUsingThreads Attribute couldn not be found.");
}
logger.debug("m_useThreads is '" + m_useThreads + "'");
if (m_useThreads) {
initializeThreadPool("10", "0", "1", false);
}
}
else {
// Need to throw an exception here!
String errMsg =
"Could not find Configuration information for application '" +
getName() +
"' check that the 'messageComponentName' property matches " +
"what's in the Deployment document.";
logger.fatal(errMsg);
throw new EnterpriseConfigurationObjectException(errMsg);
}
init(eConfig);
}
private Hashtable addObject(Hashtable map, String oName, String cClass,
String oClass,
Element eConfig2) throws EnterpriseConfigurationObjectException {
String configType = eConfig2.getName();
if (oClass == null) {
try {
java.lang.Class obj = java.lang.Class.forName(cClass);
EnterpriseConfigurationObject eco =
(EnterpriseConfigurationObject)obj.newInstance();
eco.setAppName(getName());
eco.init(eConfig2);
logger.debug("Adding " + oName + "-" + eco.getClass().getName() +
" to AppConfig for Application: " + getName());
if (eco instanceof LoggerConfig) {
initializeLog4j(eco.getProperties());
map.put(oName.toLowerCase(), eco);
}
else {
map.put(oName.toLowerCase(), eco);
}
}
catch (Exception e) {
String errMessage =
"Error initializing class " + cClass + " Exception: " +
e.getMessage();
logger.fatal(errMessage);
throw new EnterpriseConfigurationObjectException(errMessage, e);
}
}
else {
try {
logger.debug("Initializing object: " + oClass + " with config " +
cClass + " for Application " + getName());
java.lang.Class obj = java.lang.Class.forName(cClass);
java.lang.Class obj2 = java.lang.Class.forName(oClass);
EnterpriseConfigurationObject eco =
(EnterpriseConfigurationObject)obj.newInstance();
eco.setAppName(getName());
eco.init(eConfig2);
// 'instanceName' will be used by all objects so they can tell what instance
// of a particular application they are. Specifically, the PubSubConsumers
// will use this property so they can dynamically build their subscription
// name based on 'instanceName' and 'ConsumerName' as specified in the
// config document for the consumer.
eco.getProperties().put("instanceName", getInstanceName());
Class[] parms2 = { eco.getClass() };
Constructor c2 = obj2.getConstructor(parms2);
Object[] o2 = { eco }; // The config object
if (configType.equalsIgnoreCase("ProducerConfig")) {
// We may have to instantiate multiple producers and increment their names
// accordingly.
org.jdom.Attribute aNumberOfProducers =
eConfig2.getAttribute("numberOfProducers");
if (aNumberOfProducers != null) {
String sNum = aNumberOfProducers.getValue().trim();
if (sNum != null && sNum.length() > 0 &&
sNum.equals("0") == false) {
Vector vProducers = new Vector();
int numProducers = Integer.parseInt(sNum);
for (int i = 0; i < numProducers; i++) {
Object o3 =
c2.newInstance(o2); // The object we're configuring, constructed with the config object.
map.put(oName.toLowerCase() + i, o3);
vProducers.add(o3);
}
// Add the actual producer pool object to AppConfig
ProducerPool pool = new ProducerPool(vProducers);
map.put(oName.toLowerCase(), pool);
}
}
else {
Object o3 =
c2.newInstance(o2); // The object we're configuring, constructed with the config object.
map.put(oName.toLowerCase(), o3);
}
}
else {
Object o3 =
c2.newInstance(o2); // The object we're configuring, constructed with the config object.
map.put(oName.toLowerCase(), o3);
}
// Set the EnterpriseFields object in this AppConfig to be the first one
// we find in an XmlEnterpriseObject being configured. This is so
// other XmlEnterpriseObject objects will have access to this via the
// AppConfig instead of having to get it from the XmlEnterpriseObject
// that was configured by AppConfig.
/*
if (o3 instanceof XmlEnterpriseObject && getEnterpriseFields() == null) {
XmlEnterpriseObject anXeo = (XmlEnterpriseObject)o3;
setEnterpriseFields(anXeo.getEnterpriseFields());
setInputXmlLayoutManager(anXeo.getInputLayoutManager("xml"));
setOutputXmlLayoutManager(anXeo.getOutputLayoutManager("xml"));
}
*/
}
catch (Exception e) {
String errMessage =
"Error initializing class " + oClass + " with config " + cClass +
" Exception: " + e.getMessage();
logger.fatal(errMessage, e);
throw new EnterpriseConfigurationObjectException(errMessage, e);
}
}
return map;
}
private void initializeThreadPool(String maxThreads, String minThreads,
String maxIdleTime,
boolean checkBeforeProcessing) {
logger.debug("Initializing ThreadPool...");
Properties threadPoolProps = new Properties();
threadPoolProps.setProperty("maxThreads", maxThreads);
threadPoolProps.setProperty("minThreads", minThreads);
threadPoolProps.setProperty("maxIdleTime", maxIdleTime);
threadPoolProps.setProperty("checkBeforeProcessing",
new Boolean(checkBeforeProcessing).toString());
m_threadPool = new ThreadPoolImpl(threadPoolProps);
}
private class InitConfigsThread implements java.lang.Runnable {
java.util.List m_configList = null;
java.util.List m_defaultAttrs = null;
String m_listName = "";
public InitConfigsThread(String configListName,
java.util.List defaultAttrs,
java.util.List configList) {
m_configList = configList;
m_defaultAttrs = defaultAttrs;
m_listName = configListName;
}
public void run() {
ArrayList aConfigObjects = new ArrayList();
Element eDefaultParms = new Element("DefaultParms");
if (m_defaultAttrs.size() == 0) {
logger.debug("No default attributes for this config object.");
}
for (int i = 0; i < m_defaultAttrs.size(); i++) {
org.jdom.Attribute anAttr = (org.jdom.Attribute)m_defaultAttrs.get(i);
eDefaultParms.setAttribute((org.jdom.Attribute)anAttr.clone());
}
for (int j = 0; j < m_configList.size(); j++) {
Element eConfigObject = (Element)m_configList.get(j);
// If the name of eConfigObject is the same as m_configList (i.e. - ProducerConfigs/ProducerConfig
// that will be the configuration element used for the specific object we're configuring
// Otherwise, we'll add all the "default" configuration information for all configuration objects
// i.e. - parms that all ProducerConfig objects withing the ProducerConfigs container will use
// to a separate element that will be used for default values.
// Then, we'll go through all of those and configure them with default values or
// config specific values if they over-ride the default values.
if (eConfigObject.getName().equals(m_listName.substring(0,
m_listName.length() -
1))) {
aConfigObjects.add(eConfigObject);
}
else {
logger.debug("Found a default config element for the '" +
eConfigObject.getName() +
"' element. m_listName is: " + m_listName);
eDefaultParms.addContent((Element)eConfigObject.clone());
}
}
// Now, we're going to instantiate and initialize the object we're configuring (ie a ProducerConfig etc.)
for (int i = 0; i < aConfigObjects.size(); i++) {
Element eConfigObject = (Element)aConfigObjects.get(i);
try {
if (m_useThreads) {
if (m_threadPool == null) {
initializeThreadPool("10", "0", "1", false);
}
m_threadPool.addJob(new InitializeObjectThread(eDefaultParms,
eConfigObject));
}
else {
new InitializeObjectThread(eDefaultParms, eConfigObject).run();
}
}
catch (ThreadPoolException e) {
}
}
}
}
private class InitializeObjectThread implements java.lang.Runnable {
Element m_configElement = null;
Element m_defaultParms = null;
public InitializeObjectThread(Element defaultParms,
Element configElement) {
m_configElement = configElement;
m_defaultParms = defaultParms;
}
public void run() {
String configClass = null, configName = null, objectClass = null;
// Use the ConfigClass in the lower level config object, if specified
// Otherwise, use the default from the "container" level.
if (m_configElement.getChild("ConfigClass") != null) {
configClass = m_configElement.getChild("ConfigClass").getText();
}
else {
configClass = m_defaultParms.getChild("ConfigClass").getText();
}
// All config objects must have a name.
org.jdom.Attribute nameAttr = m_configElement.getAttribute("name");
configName = nameAttr.getValue();
logger.debug("Initializing object: " + configName);
// Use the object class from the lower level config object, if specified
// otherwise, try to use whatever's specified at the "container" level.
// If it's not found, that's okay.
if (m_configElement.getChild("ObjectClass") != null) {
objectClass = m_configElement.getChild("ObjectClass").getText();
}
else {
try {
objectClass = m_defaultParms.getChild("ObjectClass").getText();
}
catch (NullPointerException e) {
logger.warn(m_configElement.getName() +
" doesn't have an ObjectClass");
}
}
// Now, we're going to build one configuration object that will be used
// to instantiate and initialize the object we're configuring. We'll take
// all information from the lower level if it exists, otherwise, we'll use
// information specified at the container level.
// First the attributes
java.util.List defaultAttrList = m_defaultParms.getAttributes();
for (int i = 0; i < defaultAttrList.size(); i++) {
org.jdom.Attribute aDefaultParm =
(org.jdom.Attribute)defaultAttrList.get(i);
if (m_configElement.getAttribute(aDefaultParm.getName()) == null) {
m_configElement.setAttribute((org.jdom.Attribute)aDefaultParm.clone());
}
}
// Add the name Attribute back on...
// m_configElement.setAttribute((org.jdom.Attribute)nameAttr.clone());
// Now the elements
java.util.List defaultElementList = m_defaultParms.getChildren();
for (int i = 0; i < defaultElementList.size(); i++) {
Element eDefaultParm = (Element)defaultElementList.get(i);
if (m_configElement.getChild(eDefaultParm.getName()) == null) {
m_configElement.addContent((Element)eDefaultParm.clone());
}
}
// Instantiate and save the config object
// LoggerConfig, ProducerConfig, ConsumerConfig, MessageObjectConfig
if (configClass != null) {
try {
setObjects(addObject(getObjects(), configName, configClass,
objectClass, m_configElement));
}
catch (Exception e) {
logger.fatal("Exception Occurred configuring " + configName + " " +
objectClass + " with " + configClass);
m_exceptionOccurred = true;
m_exceptionMessage = e.getMessage();
ByteArrayOutputStream bw = new ByteArrayOutputStream();
PrintWriter pw = new PrintWriter(bw, true);
e.printStackTrace(pw);
m_exceptionStackTrace = bw.toString();
return;
}
}
}
}
/**
* Returns name and class information about all objects currently stored in this
* AppConfig object. Line feed included. Debug use only...
*
* @return String
*/
public String dumpStats() {
StringBuffer sb = new StringBuffer();
Enumeration keys = m_objects.keys();
while (keys.hasMoreElements()) {
String keyName = (String)keys.nextElement();
Object obj = m_objects.get(keyName);
String className = obj.getClass().getName();
sb.append("Name: " + keyName + " / " + className + "\n");
}
return new String(sb);
}
/**
* Recursively shuts down all objects contained within this AppConfig. This
* includes things like MessageProducers, MessageConsumers and Database Connection
* pools that may have open connections to external sources like Brokers and Databases.
**/
public void shutdown() throws EnterpriseConfigurationObjectException {
logger.debug("[AppConfig] removing all objects from space...");
logger.debug("[AppConfig] stats: " + dumpStats());
try {
Enumeration keys = getObjects().keys();
logger.debug("[AppConfig] got keys..." + keys);
while (keys.hasMoreElements()) {
String keyName = (String)keys.nextElement();
Object obj = getObjects().get(keyName);
if (obj instanceof org.openeai.afa.ScheduledApp) {
org.openeai.afa.ScheduledApp sa = (org.openeai.afa.ScheduledApp)obj;
logger.info("[AppConfig] Stopping scheduled app...");
sa.stop();
}
if (obj instanceof org.openeai.jms.consumer.MessageConsumer) {
MessageConsumer mc = (MessageConsumer)obj;
mc.stop();
}
if (obj instanceof org.openeai.jms.producer.MessageProducer) {
MessageProducer mp = (MessageProducer)obj;
mp.stop();
}
if (obj instanceof org.openeai.jms.producer.ProducerPool) {
ProducerPool p = (ProducerPool)obj;
java.util.List producers = p.getProducers();
for (int i = 0; i < producers.size(); i++) {
MessageProducer mp = (MessageProducer)producers.get(i);
mp.stop();
}
}
logger.debug("[AppConfig] removing '" + keyName + "'");
getObjects().remove(keyName);
}
}
catch (Exception e) {
throw new EnterpriseConfigurationObjectException(e.getMessage(), e);
}
}
}