org.xins.server.EngineStarter Maven / Gradle / Ivy
The newest version!
/*
* $Id: EngineStarter.java,v 1.54 2012/03/10 14:12:17 agoubard Exp $
*
* See the COPYRIGHT file for redistribution and use restrictions.
*/
package org.xins.server;
import java.util.Map;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import org.xins.common.MandatoryArgumentChecker;
import org.xins.common.Utils;
import org.xins.common.collections.InvalidPropertyValueException;
import org.xins.common.collections.MissingRequiredPropertyException;
import org.xins.common.servlet.ServletToMapConverter;
import org.xins.common.text.TextUtils;
/*import org.xins.logdoc.AbstractLog;
import org.xins.logdoc.ExceptionUtils;
import org.xins.logdoc.LogCentral;
import org.xins.logdoc.UnsupportedLocaleException;*/
/**
* XINS engine starter.
*
* @version $Revision: 1.54 $ $Date: 2012/03/10 14:12:17 $
* @author Mees Witteman
* @author Ernst de Haan
*/
final class EngineStarter {
/**
* The name of the bootstrap property that specifies the name of the
* API class to load.
*/
private static final String API_CLASS_PROPERTY = "org.xins.api.class";
/**
* The name of the bootstrap property that specifies the name of the
* API.
*/
private static final String API_NAME_PROPERTY = "org.xins.api.name";
/**
* The name of the bootstrap property that specifies the version with which the
* API was built.
*/
private static final String API_BUILD_VERSION_PROPERTY =
"org.xins.api.build.version";
/**
* The servlet config. Never null
.
*/
private ServletConfig _config;
/**
* The started time of the engine.
*/
private long _startedTime = System.currentTimeMillis();
/**
* Constructor for the EngineStarter
class.
*
* @param config
* servlet configuration, cannot be null
and is guaranteed
* to have a {@link ServletContext} associated with it.
*
* @throws IllegalArgumentException
* if config == null
.
*/
EngineStarter(ServletConfig config)
throws IllegalArgumentException {
// Check preconditions
MandatoryArgumentChecker.check("config", config);
// Store data
_config = config;
}
/**
* Constructs a new ServletException
with the specified cause.
*
* @param t
* the cause for the {@link ServletException}, can be null
.
*
* @return
* the new {@link ServletException}, that has t
registered
* as the cause for it, never null
.
*
* @see ExceptionUtils#setCause(Throwable,Throwable)
*/
static ServletException servletExceptionFor(Throwable t) {
ServletException servletException;
// If the cause is already a ServletException, use it unchanged
if (t instanceof ServletException) {
servletException = (ServletException) t;
// If a cause has been specified, then use that
} else if (t != null) {
servletException = new ServletException(t);
// Otherwise just create a vanilla ServletException
} else {
servletException = new ServletException();
}
return servletException;
}
/**
* Logs server version, warns if server version differs from common version
* and warns if the server version is not a production release.
*/
void logBootMessages() {
// Determine the ServletContext
ServletContext context = _config.getServletContext();
// Determine servlet container info
String containerInfo = context.getServerInfo();
if (containerInfo == null) {
throw Utils.logProgrammingError(
"ServletContext.getServerInfo() returned null.");
}
// Determine Java VM info
String jvmVendor = System.getProperty("java.vm.vendor");
String jvmName = System.getProperty("java.vm.name");
String jvmVersion = System.getProperty("java.vm.version");
String jvmInfo = jvmVendor + " " + jvmName + " " + jvmVersion;
// Determine operating system info
String osName = System.getProperty("os.name");
String osVersion = System.getProperty("os.version");
String osArch = System.getProperty("os.arch");
String osInfo = osName + " " + osVersion + "/" + osArch;
// Log: Bootstrapping XINS/Java Server Framework
String serverVersion = Library.getVersion();
Log.log_3200(serverVersion, containerInfo, jvmInfo, osInfo);
// Warn if Server version differs from Common version
String commonVersion = org.xins.common.Library.getVersion();
if (! serverVersion.equals(commonVersion)) {
Log.log_3226(serverVersion, commonVersion);
}
// Warn if the current XINS version is not a production version
if (! Library.isProductionRelease(serverVersion)) {
Log.log_3227(serverVersion);
}
// Warn if API build version is more recent than running version
if (Library.isProductionRelease(serverVersion)) {
String propName = API_BUILD_VERSION_PROPERTY;
String buildVersion = _config.getInitParameter(propName);
if (buildVersion == null) {
Log.log_3232(propName);
}
}
}
/**
* Constructs the API.
*
* @return The constructed API.
*
* @throws ServletException
* if the API can not be constructed from the values in the config object
*/
API constructAPI()
throws ServletException {
String apiClassName = determineAPIClassName();
Class apiClass = loadAPIClass(apiClassName);
API api = createAPI(apiClass);
checkAPIConstruction(apiClass, api);
return api;
}
/**
* Checks the construction of the API.
*
* @param apiClass
* The API class, cannot be null
.
*
* @param api
* The API instance self, cannot be null
.
*
* @throws ServletException
* if the API is null
or if the API class is not equal to
* apiClass
.
*/
private void checkAPIConstruction(Class apiClass, API api)
throws ServletException {
// Make sure that the value of the field is not null
if (api == null) {
String detail = "Creation of the API "
+ apiClass.getName()
+ " failed.";
Log.log_3208(API_CLASS_PROPERTY, apiClass.getName(), detail);
throw new ServletException();
}
// Make sure that the value of the field is an instance of that class
if (api.getClass() != apiClass) {
String detail = "Incorrect instance of the API created. "
+ api.getClass()
+ " is not an instance of class " + apiClass.getName() + ".";
Log.log_3208(API_CLASS_PROPERTY, apiClass.getName(), detail);
throw new ServletException();
}
}
/**
* Gets the API from the singleton field that is available on all API's.
*
* @param apiClass
* the api class, cannot be null
.
*
* @return
* an instance of the api object, can be null
.
*
* @throws ServletException
* if the apiClass doesn't have a singleton field or
* if the value of the field can not be cast to the API class.
*/
private API createAPI(Class apiClass)
throws ServletException {
API api;
try {
api = (API) apiClass.newInstance();
} catch (Throwable exception) {
String detail = "Caught unexpected "
+ exception.getClass().getName()
+ " while creating class "
+ apiClass.getName()
+ '.';
Utils.logProgrammingError(detail, exception);
Log.log_3208(API_CLASS_PROPERTY, apiClass.getName(), detail);
throw servletExceptionFor(exception);
}
return api;
}
/**
* Loads the class with the given name and performs checks on the loaded
* class.
*
* @param apiClassName
* the name of the API class that should be loaded, cannot be
* null
.
*
* @return
* the loaded API class, never null
.
*
* @throws IllegalArgumentException
* if apiClassName == null
.
*
* @throws ServletException
* if the API class loading failed.
*/
private Class loadAPIClass(String apiClassName)
throws IllegalArgumentException, ServletException {
// Check preconditions
MandatoryArgumentChecker.check("apiClassName", apiClassName);
// Load the API class
Class apiClass;
try {
apiClass = Class.forName(apiClassName, true, Utils.getContextClassLoader());
} catch (Throwable exception) {
Log.log_3207(exception, API_CLASS_PROPERTY, apiClassName);
throw servletExceptionFor(exception);
}
// Check that the loaded API class is derived from the API base class
if (! API.class.isAssignableFrom(apiClass)) {
String detail = "Class "
+ apiClassName
+ " is not derived from "
+ API.class.getName()
+ '.';
Log.log_3208(API_CLASS_PROPERTY, apiClassName, detail);
throw new ServletException();
}
return apiClass;
}
/**
* Determines the API class name from the config file.
*
* @return The API class name
*
* @throws ServletException if the class name could not be determined from
* the init parameters.
*/
private String determineAPIClassName()
throws ServletException {
String apiClassName = _config.getInitParameter(API_CLASS_PROPERTY);
apiClassName = TextUtils.isEmpty(apiClassName)
? null
: apiClassName.trim();
if (apiClassName == null) {
Log.log_3206(API_CLASS_PROPERTY);
throw new ServletException();
}
return apiClassName;
}
/**
* Calls the bootstrap on the API and logs exceptions in case of an error.
*
* @param api
* the API to bootstrap, never null
.
*
* @return
* a {@link Map} for the bootstrap properties, never
* null
.
*
* @throws ServletException
* if the bootstrap of the api fails.
*/
Map bootstrap(API api) throws ServletException {
// Convert ServletConfig to Map
Map properties = ServletToMapConverter.servletConfigToMap(_config);
// Determine at what level should the stack traces be displayed
/*String stackTraceAtMessageLevel = properties.get(LogCentral.LOG_STACK_TRACE_AT_MESSAGE_LEVEL);
if ("true".equals(stackTraceAtMessageLevel)) {
LogCentral.setStackTraceAtMessageLevel(true);
} else if ("false".equals(stackTraceAtMessageLevel)) {
LogCentral.setStackTraceAtMessageLevel(false);
} else if (stackTraceAtMessageLevel != null) {
throw new ServletException("Incorrect value for the " +
LogCentral.LOG_STACK_TRACE_AT_MESSAGE_LEVEL + " bootstrap property.");
}*/
// Bootstrap the API self
Throwable caught;
try {
api.bootstrap(properties);
caught = null;
// Missing required property
} catch (MissingRequiredPropertyException exception) {
Log.log_3209(exception.getPropertyName(), exception.getDetail());
caught = exception;
// Invalid property value
} catch (InvalidPropertyValueException exception) {
Log.log_3210(exception.getPropertyName(),
exception.getPropertyValue(),
exception.getReason());
caught = exception;
// Other bootstrap error
} catch (Throwable exception) {
Log.log_3211(exception);
caught = exception;
}
// Throw a ServletException if the bootstrap failed
if (caught != null) {
ServletException se = new ServletException("API bootstrap failed.", caught);
throw se;
}
return properties;
}
/**
* Attempts to load the logdoc class and performs checks on the class.
*
* @throws ServletException
* If the log doc class can not be loaded.
*/
void loadLogdoc() throws ServletException {
/*String logdocClassName = determineLogdocName();
try {
// Attempt to load the Logdoc 'Log' class. This should execute the
// static initializer, which is what we want.
Class logdocClass = Class.forName(logdocClassName);
// Is the loaded class really a Logdoc 'Log' class or just some
// other class that is coincedentally called 'Log' ?
// If it is, then the API indeed uses Logdoc logging
if (AbstractLog.class.isAssignableFrom(logdocClass)) {
Log.log_3233();
// The API does not use Logdoc logging
} else {
Log.log_3234();
}
// There is no 'Log' class in the API package
} catch (ClassNotFoundException cnfe) {
Log.log_3234();
// The locale is not supported
} catch (UnsupportedLocaleException exception) {
Log.log_3309(exception.getLocale());
throw servletExceptionFor(exception);
// Other unexpected exception
} catch (Throwable exception) {
Utils.logProgrammingError("Unexpected exception while loading Logdoc Log class for API.", exception);
}*/
}
/**
* Determines the name of the Logdoc Log class for the API. If there is a
* Logdoc Log class for the API, then it should match the returned
* fully-qualified class name.
*
* @return
* the fully-qualified name of the Logdoc Log class, never
* null
.
*/
private String determineLogdocName() {
// Determine the name of the API class
String apiClassName = _config.getInitParameter(API_CLASS_PROPERTY);
// Determine the class prefix, which is everything before the
// unqualified class name
String classPrefix;
int lastDot = apiClassName.lastIndexOf('.');
if (lastDot < 0) {
classPrefix = "";
} else {
classPrefix = apiClassName.substring(0, lastDot + 1);
}
// The name of the Logdoc Log class is always "Log"
String logdocClassName = classPrefix + "Log";
return logdocClassName;
}
/**
* Determines the API name.
*
* @return
* the API name, or "-"
if unknown, never
* null
.
*
* @throws ServletException
* if the API name is not set.
*/
String determineAPIName() throws ServletException {
// Determine the name of the API
String apiName = _config.getInitParameter(API_NAME_PROPERTY);
if (apiName != null) {
apiName = apiName.trim();
}
// If the name is not set, then return a hyphen instead
if (TextUtils.isEmpty(apiName)) {
Log.log_3232(API_NAME_PROPERTY);
throw new ServletException("The API name is not set.");
} else {
apiName = apiName.trim();
Log.log_3235(apiName);
}
return apiName;
}
/**
* Registers the API MBean.
*
* @param api
* the API, never null
.
*/
void registerMBean(API api) {
try {
APIManager.registerMBean(api);
// If for any reason it doesn't work, ignore.
// For example if the server is running on Java 1.4 a ClassNotFoundException may be thrown.
} catch (Throwable ex) {
Log.log_3249(ex.getMessage());
}
}
/**
* The time when the engine has been started.
*
* @return
* when the engine was started (in milliseconds since the EPOCH).
*/
long getStartedTime() {
return _startedTime;
}
}