blob: 4cffe6cd6b293298ea2c5df9c37aecaecc35558c [file] [log] [blame]
/*
* Copyright 2001-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.axis;
import org.apache.axis.components.logger.LogFactory;
import org.apache.axis.encoding.TypeMappingRegistry;
import org.apache.axis.encoding.TypeMappingImpl;
import org.apache.axis.handlers.BasicHandler;
import org.apache.axis.handlers.soap.SOAPService;
import org.apache.axis.session.Session;
import org.apache.axis.session.SimpleSession;
import org.apache.axis.utils.JavaUtils;
import org.apache.axis.utils.Messages;
import org.apache.axis.utils.cache.ClassCache;
import org.apache.commons.logging.Log;
import javax.xml.namespace.QName;
import javax.xml.rpc.server.ServiceLifecycle;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
/**
* An <code>AxisEngine</code> is the base class for AxisClient and
* AxisServer. Handles common functionality like dealing with the
* handler/service registries and loading properties.
*
* @author Glen Daniels (gdaniels@apache.org)
* @author Glyn Normington (glyn@apache.org)
*/
public abstract class AxisEngine extends BasicHandler
{
/**
* The <code>Log</code> for all message logging.
*/
protected static Log log =
LogFactory.getLog(AxisEngine.class.getName());
// Engine property names
public static final String PROP_XML_DECL = "sendXMLDeclaration";
public static final String PROP_DEBUG_LEVEL = "debugLevel";
public static final String PROP_DEBUG_FILE = "debugFile";
public static final String PROP_DOMULTIREFS = "sendMultiRefs";
public static final String PROP_DISABLE_PRETTY_XML = "disablePrettyXML";
public static final String PROP_ENABLE_NAMESPACE_PREFIX_OPTIMIZATION = "enableNamespacePrefixOptimization";
public static final String PROP_PASSWORD = "adminPassword";
public static final String PROP_SYNC_CONFIG = "syncConfiguration";
public static final String PROP_SEND_XSI = "sendXsiTypes";
public static final String PROP_ATTACHMENT_DIR = "attachments.Directory";
public static final String PROP_ATTACHMENT_IMPLEMENTATION = "attachments.implementation" ;
public static final String PROP_ATTACHMENT_CLEANUP = "attachment.DirectoryCleanUp";
public static final String PROP_DEFAULT_CONFIG_CLASS = "axis.engineConfigClass";
public static final String PROP_SOAP_VERSION = "defaultSOAPVersion";
public static final String PROP_SOAP_ALLOWED_VERSION = "singleSOAPVersion";
public static final String PROP_TWOD_ARRAY_ENCODING = "enable2DArrayEncoding";
public static final String PROP_XML_ENCODING = "axis.xmlEncoding";
public static final String PROP_XML_REUSE_SAX_PARSERS = "axis.xml.reuseParsers";
public static final String PROP_BYTE_BUFFER_BACKING = "axis.byteBuffer.backing";
public static final String PROP_BYTE_BUFFER_CACHE_INCREMENT = "axis.byteBuffer.cacheIncrement";
public static final String PROP_BYTE_BUFFER_RESIDENT_MAX_SIZE = "axis.byteBuffer.residentMaxSize";
public static final String PROP_BYTE_BUFFER_WORK_BUFFER_SIZE = "axis.byteBuffer.workBufferSize";
public static final String PROP_EMIT_ALL_TYPES = "emitAllTypesInWSDL";
/**
* Set this property to 'true' when you want Axis to avoid soap encoded
* types to work around a .NET problem where it wont accept soap encoded
* types for a (soap encoded!) array.
*/
public static final String PROP_DOTNET_SOAPENC_FIX = "dotNetSoapEncFix";
/** Compliance with WS-I Basic Profile. */
public static final String PROP_BP10_COMPLIANCE = "ws-i.bp10Compliance";
public static final String DEFAULT_ATTACHMENT_IMPL="org.apache.axis.attachments.AttachmentsImpl";
public static final String ENV_ATTACHMENT_DIR = "axis.attachments.Directory";
public static final String ENV_SERVLET_REALPATH = "servlet.realpath";
public static final String ENV_SERVLET_CONTEXT = "servletContext";
// Default admin. password
private static final String DEFAULT_ADMIN_PASSWORD = "admin";
/** Our go-to guy for configuration... */
protected EngineConfiguration config;
/** Has the user changed the password yet? True if they have. */
protected boolean _hasSafePassword = false;
/**
* Should we save the engine config each time we modify it? True if we
* should.
*/
protected boolean shouldSaveConfig = false;
/** Java class cache. */
protected transient ClassCache classCache = new ClassCache();
/**
* This engine's Session. This Session supports "application scope"
* in the Apache SOAP sense... if you have a service with "application
* scope", have it store things in this Session.
*/
private Session session = new SimpleSession();
/**
* What actor URIs hold for the entire engine? Find them here.
*/
private ArrayList actorURIs = new ArrayList();
/**
* Thread local storage used for locating the active message context.
* This information is only valid for the lifetime of this request.
*/
private static ThreadLocal currentMessageContext = new ThreadLocal();
/**
* Set the active message context.
*
* @param mc - the new active message context.
*/
protected static void setCurrentMessageContext(MessageContext mc) {
currentMessageContext.set(mc);
}
/**
* Get the active message context.
*
* @return the current active message context
*/
public static MessageContext getCurrentMessageContext() {
return (MessageContext) currentMessageContext.get();
}
/**
* Construct an AxisEngine using the specified engine configuration.
*
* @param config the EngineConfiguration for this engine
*/
public AxisEngine(EngineConfiguration config)
{
this.config = config;
init();
}
/**
* Initialize the engine. Multiple calls will (may?) return the engine to
* the intialized state.
*/
public void init() {
if (log.isDebugEnabled()) {
log.debug("Enter: AxisEngine::init");
}
// The SOAP/XSD stuff is in the default TypeMapping of the TypeMappingRegistry.
//getTypeMappingRegistry().setParent(SOAPTypeMappingRegistry.getSingletonDelegate());
try {
config.configureEngine(this);
} catch (Exception e) {
throw new InternalException(e);
}
/*Set the default attachment implementation */
setOptionDefault(PROP_ATTACHMENT_IMPLEMENTATION,
AxisProperties.getProperty("axis." + PROP_ATTACHMENT_IMPLEMENTATION ));
setOptionDefault(PROP_ATTACHMENT_IMPLEMENTATION, DEFAULT_ATTACHMENT_IMPL);
// Check for the property "dotnetsoapencfix" which will turn
// off soap encoded types to work around a bug in .NET where
// it wont accept soap encoded array types.
final Object dotnet = getOption(PROP_DOTNET_SOAPENC_FIX);
if (JavaUtils.isTrue(dotnet)) {
// This is a static property of the type mapping
// that will ignore SOAPENC types when looking up
// QNames of java types.
TypeMappingImpl.dotnet_soapenc_bugfix = true;
}
if (log.isDebugEnabled()) {
log.debug("Exit: AxisEngine::init");
}
}
/**
* Cleanup routine removes application scoped objects.
*
* There is a small risk of this being called more than once
* so the cleanup should be designed to resist that event.
*/
public void cleanup() {
super.cleanup();
// Let any application-scoped service objects know that we're going
// away...
Enumeration keys = session.getKeys();
if (keys != null) {
while (keys.hasMoreElements()) {
String key = (String)keys.nextElement();
Object obj = session.get(key);
if (obj != null && obj instanceof ServiceLifecycle) {
((ServiceLifecycle)obj).destroy();
}
session.remove(key);
}
}
}
/** Write out our engine configuration.
*/
public void saveConfiguration()
{
if (!shouldSaveConfig)
return;
try {
config.writeEngineConfig(this);
} catch (Exception e) {
log.error(Messages.getMessage("saveConfigFail00"), e);
}
}
/**
* Get the <code>EngineConfiguration</code> used throughout this
* <code>AxisEngine</code> instance.
*
* @return the engine configuration instance
*/
public EngineConfiguration getConfig() {
return config;
}
/**
* Discover if this <code>AxisEngine</code> has a safe password.
*
* @return true if it is safe, false otherwise
*/
public boolean hasSafePassword()
{
return _hasSafePassword;
}
/**
* Set the administration password.
*
* @param pw the literal value of the password as a <code>String</code>
*/
public void setAdminPassword(String pw)
{
setOption(PROP_PASSWORD, pw);
_hasSafePassword = true;
saveConfiguration();
}
/**
* Set the flag that controls if the configuration should be saved.
*
* @param shouldSaveConfig true if the configuration should be changed,
* false otherwise
*/
public void setShouldSaveConfig(boolean shouldSaveConfig)
{
this.shouldSaveConfig = shouldSaveConfig;
}
// fixme: could someone who knows double-check I've got the semantics of
// this right?
/**
* Get the <code>Handler</code> for a particular local name.
*
* @param name the local name of the request type
* @return the <code>Handler</code> for this request type
* @throws AxisFault
*/
public Handler getHandler(String name) throws AxisFault
{
try {
return config.getHandler(new QName(null, name));
} catch (ConfigurationException e) {
throw new AxisFault(e);
}
}
// fixme: could someone who knows double-check I've got the semantics of
// this right?
/**
* Get the <code>SOAPService</code> for a particular local name.
*
* @param name the local name of the request type
* @return the <code>SOAPService</code> for this request type
* @throws AxisFault
*/
public SOAPService getService(String name) throws AxisFault
{
try {
return config.getService(new QName(null, name));
} catch (ConfigurationException e) {
try {
return config.getServiceByNamespaceURI(name);
} catch (ConfigurationException e1) {
throw new AxisFault(e);
}
}
}
/**
* Get the <code>Handler</code> that implements the transport for a local
* name.
*
* @param name the local name to fetch the transport for
* @return a <code>Handler</code> for this local name
* @throws AxisFault
*/
public Handler getTransport(String name) throws AxisFault
{
try {
return config.getTransport(new QName(null, name));
} catch (ConfigurationException e) {
throw new AxisFault(e);
}
}
/**
* Get the <code>TypeMappingRegistry</code> for this axis engine.
*
* @return the <code>TypeMappingRegistry</code> if possible, or null if
* there is any error resolving it
*/
public TypeMappingRegistry getTypeMappingRegistry()
{
TypeMappingRegistry tmr = null;
try {
tmr = config.getTypeMappingRegistry();
} catch (ConfigurationException e) {
log.error(Messages.getMessage("axisConfigurationException00"), e);
}
return tmr;
}
/**
* Get the global request <code>Handler</code>.
*
* @return the <code>Handler</code> used for global requests
* @throws ConfigurationException
*/
public Handler getGlobalRequest()
throws ConfigurationException
{
return config.getGlobalRequest();
}
/**
* Get the global respones <code>Handler</code>.
*
* @return the <code>Handler</code> used for global responses
* @throws ConfigurationException
*/
public Handler getGlobalResponse()
throws ConfigurationException
{
return config.getGlobalResponse();
}
// fixme: publishing this as ArrayList prevents us moving to another
// List impl later
/**
* Get a list of actor URIs that hold for the entire engine.
*
* @return an <code>ArrayList</code> of all actor URIs as
* <code>Strings</code>
*/
public ArrayList getActorURIs()
{
return (ArrayList)actorURIs.clone();
}
/**
* Add an actor by uri that will hold for the entire engine.
*
* @param uri a <code>String</code> giving the uri of the actor to add
*/
public void addActorURI(String uri)
{
actorURIs.add(uri);
}
/**
* Remove an actor by uri that will hold for the entire engine.
*
* @param uri a <code>String</code> giving the uri of the actor to remove
*/
public void removeActorURI(String uri)
{
actorURIs.remove(uri);
}
/**
* Client engine access.
* <p>
* An AxisEngine may define another specific AxisEngine to be used
* by newly created Clients. For instance, a server may
* create an AxisClient and allow deployment to it. Then
* the server's services may access the AxisClient's deployed
* handlers and transports.
*
* @return an <code>AxisEngine</code> that is the client engine
*/
public abstract AxisEngine getClientEngine ();
/**
* Administration and management APIs
*
* These can get called by various admin adapters, such as JMX MBeans,
* our own Admin client, web applications, etc...
*
*/
/**
* List of options which should be converted from Strings to Booleans
* automatically. Note that these options are common to all XML
* web services.
*/
private static final String [] BOOLEAN_OPTIONS = new String [] {
PROP_DOMULTIREFS, PROP_SEND_XSI, PROP_XML_DECL,
PROP_DISABLE_PRETTY_XML,
PROP_ENABLE_NAMESPACE_PREFIX_OPTIMIZATION
};
/**
* Normalise the engine's options.
* <p>
* Convert boolean options from String to Boolean and default
* any ommitted boolean options to TRUE. Default the admin.
* password.
*
* @param handler the <code>Handler</code> to normalise; instances of
* <code>AxisEngine</code> get extra data normalised
*/
public static void normaliseOptions(Handler handler) {
// Convert boolean options to Booleans so we don't need to use
// string comparisons. Default is "true".
for (int i = 0; i < BOOLEAN_OPTIONS.length; i++) {
Object val = handler.getOption(BOOLEAN_OPTIONS[i]);
if (val != null) {
if (val instanceof Boolean)
continue;
if (JavaUtils.isFalse(val)) {
handler.setOption(BOOLEAN_OPTIONS[i], Boolean.FALSE);
continue;
}
} else {
if (!(handler instanceof AxisEngine))
continue;
}
// If it was null or not "false"...
handler.setOption(BOOLEAN_OPTIONS[i], Boolean.TRUE);
}
// Deal with admin password's default value.
if (handler instanceof AxisEngine) {
AxisEngine engine = (AxisEngine)handler;
if (!engine.setOptionDefault(PROP_PASSWORD,
DEFAULT_ADMIN_PASSWORD)) {
engine.setAdminPassword(
(String)engine.getOption(PROP_PASSWORD));
}
}
}
/**
* (Re-)load the global options from the registry.
*
* @throws ConfigurationException
*/
public void refreshGlobalOptions() throws ConfigurationException {
Hashtable globalOptions = config.getGlobalOptions();
if (globalOptions != null)
setOptions(globalOptions);
normaliseOptions(this);
// fixme: If we change actorURIs to List, this copy constructor can
// go away...
actorURIs = new ArrayList(config.getRoles());
}
/**
* Get the <code>Session</code> object associated with the application
* session.
*
* @return a <code>Session</code> scoped to the application
*/
public Session getApplicationSession () {
return session;
}
/**
* Get the <code>ClassCache</code> associated with this engine.
*
* @return the class cache
*/
public ClassCache getClassCache() {
return classCache;
}
}