/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.catalina.core;
/** * Standard implementation of the <b>Server</b> interface, available for use (but not required) when deploying and * starting Catalina. * * @author Craig R. McClanahan
*/ publicfinalclass StandardServer extends LifecycleMBeanBase implements Server {
privatestaticfinal Log log = LogFactory.getLog(StandardServer.class); privatestaticfinal StringManager sm = StringManager.getManager(StandardServer.class);
/** * The naming context listener for this web application.
*/ privatefinal NamingContextListener namingContextListener;
/** * The port number on which we wait for shutdown commands.
*/ privateint port = 8005;
privateint portOffset = 0;
/** * The address on which we wait for shutdown commands.
*/ private String address = "localhost";
/** * A random number generator that is <strong>only</strong> used if the shutdown command string is longer than 1024 * characters.
*/ private Random random = null;
/** * The set of Services associated with this Server.
*/ private Service[] services = new Service[0]; privatefinal Object servicesLock = new Object();
/** * The shutdown command string we are looking for.
*/ private String shutdown = "SHUTDOWN";
/** * The property change support for this component.
*/ final PropertyChangeSupport support = new PropertyChangeSupport(this);
privatevolatileboolean stopAwait = false;
private Catalina catalina = null;
private ClassLoader parentClassLoader = null;
/** * Thread that currently is inside our await() method.
*/ privatevolatileThread awaitThread = null;
/** * Server socket that is used to wait for the shutdown command.
*/ privatevolatile ServerSocket awaitSocket = null;
private File catalinaHome = null;
private File catalinaBase = null;
privatefinal Object namingToken = new Object();
/** * The number of threads available to process utility tasks in this service.
*/ privateint utilityThreads = 2;
@Override public Object getNamingToken() { return namingToken;
}
/** * Return the global naming resources context.
*/
@Override public javax.naming.Context getGlobalNamingContext() { returnthis.globalNamingContext;
}
/** * Set the global naming resources context. * * @param globalNamingContext The new global naming resource context
*/ publicvoid setGlobalNamingContext(javax.naming.Context globalNamingContext) { this.globalNamingContext = globalNamingContext;
}
/** * Return the global naming resources.
*/
@Override public NamingResourcesImpl getGlobalNamingResources() { returnthis.globalNamingResources;
}
/** * Set the global naming resources. * * @param globalNamingResources The new global naming resources
*/
@Override publicvoid setGlobalNamingResources(NamingResourcesImpl globalNamingResources) {
/** * Report the current Tomcat Server Release number * * @return Tomcat release identifier
*/ public String getServerInfo() { return ServerInfo.getServerInfo();
}
/** * Return the current server built timestamp * * @return server built timestamp.
*/ public String getServerBuilt() { return ServerInfo.getServerBuilt();
}
/** * Return the current server's version number. * * @return server's version number.
*/ public String getServerNumber() { return ServerInfo.getServerNumber();
}
/** * Return the port number we listen to for shutdown commands.
*/
@Override publicint getPort() { returnthis.port;
}
/** * Set the port number we listen to for shutdown commands. * * @param port The new port number
*/
@Override publicvoid setPort(int port) { this.port = port;
}
@Override publicint getPortWithOffset() { // Non-positive port values have special meanings and the offset should // not apply. int port = getPort(); if (port > 0) { return port + getPortOffset();
} else { return port;
}
}
/** * Return the address on which we listen to for shutdown commands.
*/
@Override public String getAddress() { returnthis.address;
}
/** * Set the address on which we listen to for shutdown commands. * * @param address The new address
*/
@Override publicvoid setAddress(String address) { this.address = address;
}
/** * Return the shutdown command string we are waiting for.
*/
@Override public String getShutdown() { returnthis.shutdown;
}
/** * Set the shutdown command we are waiting for. * * @param shutdown The new shutdown command
*/
@Override publicvoid setShutdown(String shutdown) { this.shutdown = shutdown;
}
/** * Return the outer Catalina startup/shutdown component if present.
*/
@Override public Catalina getCatalina() { return catalina;
}
/** * Set the outer Catalina startup/shutdown component if present.
*/
@Override publicvoid setCatalina(Catalina catalina) { this.catalina = catalina;
}
/** * Handles the special values.
*/ privatestaticint getUtilityThreadsInternal(int utilityThreads) { int result = utilityThreads; if (result <= 0) {
result = Runtime.getRuntime().availableProcessors() + result; if (result < 2) {
result = 2;
}
} return result;
}
@Override publicvoid setUtilityThreads(int utilityThreads) { // Use local copies to ensure thread safety int oldUtilityThreads = this.utilityThreads; if (getUtilityThreadsInternal(utilityThreads) < getUtilityThreadsInternal(oldUtilityThreads)) { return;
} this.utilityThreads = utilityThreads; synchronized (utilityExecutorLock) { if (oldUtilityThreads != utilityThreads && utilityExecutor != null) {
reconfigureUtilityExecutor(getUtilityThreadsInternal(utilityThreads));
}
}
}
/* * Callers must be holding utilityExecutorLock
*/ privatevoid reconfigureUtilityExecutor(int threads) { // The ScheduledThreadPoolExecutor doesn't use MaximumPoolSize, only CorePoolSize is available if (utilityExecutor != null) {
utilityExecutor.setCorePoolSize(threads);
} else {
ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(threads, new TaskThreadFactory("Catalina-utility-", utilityThreadsAsDaemon, Thread.MIN_PRIORITY));
scheduledThreadPoolExecutor.setKeepAliveTime(10, TimeUnit.SECONDS);
scheduledThreadPoolExecutor.setRemoveOnCancelPolicy(true);
scheduledThreadPoolExecutor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
utilityExecutor = scheduledThreadPoolExecutor;
utilityExecutorWrapper = new org.apache.tomcat.util.threads.ScheduledThreadPoolExecutor(utilityExecutor);
}
}
/** * Get if the utility threads are daemon threads. * * @return the threads daemon flag
*/ publicboolean getUtilityThreadsAsDaemon() { return utilityThreadsAsDaemon;
}
/** * Set the utility threads daemon flag. The default value is true. * * @param utilityThreadsAsDaemon the new thread daemon flag
*/ publicvoid setUtilityThreadsAsDaemon(boolean utilityThreadsAsDaemon) { this.utilityThreadsAsDaemon = utilityThreadsAsDaemon;
}
/** * @return The period between two lifecycle events, in seconds
*/ publicint getPeriodicEventDelay() { return periodicEventDelay;
}
/** * Set the new period between two lifecycle events in seconds. * * @param periodicEventDelay The period in seconds, negative or zero will disable events
*/ publicvoid setPeriodicEventDelay(int periodicEventDelay) { this.periodicEventDelay = periodicEventDelay;
}
// --------------------------------------------------------- Server Methods
/** * Add a new Service to the set of defined Services. * * @param service The Service to be added
*/
@Override publicvoid addService(Service service) {
/** * Wait until a proper shutdown command is received, then return. This keeps the main thread alive - the thread pool * listening for http connections is daemon threads.
*/
@Override publicvoid await() { // Negative values - don't wait on port - tomcat is embedded or we just don't like ports if (getPortWithOffset() == -2) { // undocumented yet - for embedding apps that are around, alive. return;
} Thread currentThread = Thread.currentThread(); if (getPortWithOffset() == -1) { try {
awaitThread = currentThread; while (!stopAwait) { try { Thread.sleep(10000);
} catch (InterruptedException ex) { // continue and check the flag
}
}
} finally {
awaitThread = null;
} return;
}
// Set up a server socket to wait on try {
awaitSocket = new ServerSocket(getPortWithOffset(), 1, InetAddress.getByName(address));
} catch (IOException e) {
log.error(sm.getString("standardServer.awaitSocket.fail", address, String.valueOf(getPortWithOffset()),
String.valueOf(getPort()), String.valueOf(getPortOffset())), e); return;
}
try {
awaitThread = currentThread;
// Loop waiting for a connection and a valid command while (!stopAwait) {
ServerSocket serverSocket = awaitSocket; if (serverSocket == null) { break;
}
// Wait for the next connection
Socket socket = null;
StringBuilder command = new StringBuilder(); try {
InputStream stream; long acceptStartTime = System.currentTimeMillis(); try {
socket = serverSocket.accept();
socket.setSoTimeout(10 * 1000); // Ten seconds
stream = socket.getInputStream();
} catch (SocketTimeoutException ste) { // This should never happen but bug 56684 suggests that // it does.
log.warn(sm.getString("standardServer.accept.timeout", Long.valueOf(System.currentTimeMillis() - acceptStartTime)), ste); continue;
} catch (AccessControlException ace) {
log.warn(sm.getString("standardServer.accept.security"), ace); continue;
} catch (IOException e) { if (stopAwait) { // Wait was aborted with socket.close() break;
}
log.error(sm.getString("standardServer.accept.error"), e); break;
}
// Read a set of characters from the socket int expected = 1024; // Cut off to avoid DoS attack while (expected < shutdown.length()) { if (random == null) {
random = new Random();
}
expected += random.nextInt() % 1024;
} while (expected > 0) { int ch = -1; try {
ch = stream.read();
} catch (IOException e) {
log.warn(sm.getString("standardServer.accept.readError"), e);
ch = -1;
} // Control character or EOF (-1) terminates loop if (ch < 32 || ch == 127) { break;
}
command.append((char) ch);
expected--;
}
} finally { // Close the socket now that we are done with it try { if (socket != null) {
socket.close();
}
} catch (IOException e) { // Ignore
}
}
// Match against our command string boolean match = command.toString().equals(shutdown); if (match) {
log.info(sm.getString("standardServer.shutdownViaPort")); break;
} else {
log.warn(sm.getString("standardServer.invalidShutdownCommand", command.toString()));
}
}
} finally {
ServerSocket serverSocket = awaitSocket;
awaitThread = null;
awaitSocket = null;
// Close the server socket and return if (serverSocket != null) { try {
serverSocket.close();
} catch (IOException e) { // Ignore
}
}
}
}
/** * @return the specified Service (if it exists); otherwise return <code>null</code>. * * @param name Name of the Service to be returned
*/
@Override public Service findService(String name) { if (name == null) { returnnull;
} synchronized (servicesLock) { for (Service service : services) { if (name.equals(service.getName())) { return service;
}
}
} returnnull;
}
/** * @return The array of Services defined within this Server.
*/
@Override public Service[] findServices() { return services;
}
/** * @return the JMX service names.
*/ public ObjectName[] getServiceNames() {
ObjectName[] onames = new ObjectName[services.length]; for (int i = 0; i < services.length; i++) {
onames[i] = ((StandardService) services[i]).getObjectName();
} return onames;
}
/** * Remove the specified Service from the set associated from this Server. * * @param service The Service to be removed
*/
@Override publicvoid removeService(Service service) {
synchronized (servicesLock) { int j = -1; for (int i = 0; i < services.length; i++) { if (service == services[i]) {
j = i; break;
}
} if (j < 0) { return;
} try {
services[j].stop();
} catch (LifecycleException e) { // Ignore
} int k = 0;
Service[] results = new Service[services.length - 1]; for (int i = 0; i < services.length; i++) { if (i != j) {
results[k++] = services[i];
}
}
services = results;
// Report this property change to interested listeners
support.firePropertyChange("service", service, null);
}
}
@Override public File getCatalinaBase() { if (catalinaBase != null) { return catalinaBase;
}
// --------------------------------------------------------- Public Methods
/** * Add a property change listener to this component. * * @param listener The listener to add
*/ publicvoid addPropertyChangeListener(PropertyChangeListener listener) {
support.addPropertyChangeListener(listener);
}
/** * Remove a property change listener from this component. * * @param listener The listener to remove
*/ publicvoid removePropertyChangeListener(PropertyChangeListener listener) {
support.removePropertyChangeListener(listener);
}
/** * Return a String representation of this component.
*/
@Override public String toString() { return"StandardServer[" + getPort() + ']';
}
/** * Write the configuration information for this entire <code>Server</code> out to the server.xml configuration file. * * @exception InstanceNotFoundException if the managed resource object cannot be found * @exception MBeanException if the initializer of the object throws an exception, or * persistence is not supported * @exception javax.management.RuntimeOperationsException if an exception is reported by the persistence mechanism
*/ publicsynchronizedvoid storeConfig() throws InstanceNotFoundException, MBeanException { try { // Note: Hard-coded domain used since this object is per Server/JVM
ObjectName sname = new ObjectName("Catalina:type=StoreConfig");
MBeanServer server = Registry.getRegistry(null, null).getMBeanServer(); if (server.isRegistered(sname)) {
server.invoke(sname, "storeConfig", null, null);
} else {
log.error(sm.getString("standardServer.storeConfig.notAvailable", sname));
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString("standardServer.storeConfig.error"), t);
}
}
/** * Write the configuration information for <code>Context</code> out to the specified configuration file. * * @param context the context which should save its configuration * * @exception InstanceNotFoundException if the managed resource object cannot be found * @exception MBeanException if the initializer of the object throws an exception or * persistence is not supported * @exception javax.management.RuntimeOperationsException if an exception is reported by the persistence mechanism
*/ publicsynchronizedvoid storeContext(Context context) throws InstanceNotFoundException, MBeanException { try { // Note: Hard-coded domain used since this object is per Server/JVM
ObjectName sname = new ObjectName("Catalina:type=StoreConfig");
MBeanServer server = Registry.getRegistry(null, null).getMBeanServer(); if (server.isRegistered(sname)) {
server.invoke(sname, "store", new Object[] { context }, new String[] { "java.lang.String" });
} else {
log.error(sm.getString("standardServer.storeConfig.notAvailable", sname));
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString("standardServer.storeConfig.contextError", context.getName()), t);
}
}
/** * @return <code>true</code> if naming should be used.
*/ privateboolean isUseNaming() { boolean useNaming = true; // Reading the "catalina.useNaming" environment variable
String useNamingProperty = System.getProperty("catalina.useNaming"); if (useNamingProperty != null && useNamingProperty.equals("false")) {
useNaming = false;
} return useNaming;
}
/** * Start nested components ({@link Service}s) and implement the requirements of * {@link org.apache.catalina.util.LifecycleBase#startInternal()}. * * @exception LifecycleException if this component detects a fatal error that prevents this component from being * used
*/
@Override protectedvoid startInternal() throws LifecycleException {
privatevoid startPeriodicLifecycleEvent() { if (periodicLifecycleEventFuture == null || periodicLifecycleEventFuture.isDone()) { if (periodicLifecycleEventFuture != null && periodicLifecycleEventFuture.isDone()) { // There was an error executing the scheduled task, get it and log it try {
periodicLifecycleEventFuture.get();
} catch (InterruptedException | ExecutionException e) {
log.error(sm.getString("standardServer.periodicEventError"), e);
}
}
periodicLifecycleEventFuture =
getUtilityExecutor().scheduleAtFixedRate(() -> fireLifecycleEvent(PERIODIC_EVENT, null),
periodicEventDelay, periodicEventDelay, TimeUnit.SECONDS);
}
}
/** * Stop nested components ({@link Service}s) and implement the requirements of * {@link org.apache.catalina.util.LifecycleBase#stopInternal()}. * * @exception LifecycleException if this component detects a fatal error that needs to be reported
*/
@Override protectedvoid stopInternal() throws LifecycleException {
setState(LifecycleState.STOPPING);
if (monitorFuture != null) {
monitorFuture.cancel(true);
monitorFuture = null;
} if (periodicLifecycleEventFuture != null) {
periodicLifecycleEventFuture.cancel(false);
periodicLifecycleEventFuture = null;
}
fireLifecycleEvent(CONFIGURE_STOP_EVENT, null);
// Stop our defined Services for (Service service : services) {
service.stop();
}
/** * Invoke a pre-startup initialization. This is used to allow connectors to bind to restricted ports under Unix * operating environments.
*/
@Override protectedvoid initInternal() throws LifecycleException {
super.initInternal();
// Register global String cache // Note although the cache is global, if there are multiple Servers // present in the JVM (may happen when embedding) then the same cache // will be registered under multiple names
onameStringCache = register(new StringCache(), "type=StringCache");
// Register the MBeanFactory
MBeanFactory factory = new MBeanFactory();
factory.setContainer(this);
onameMBeanFactory = register(factory, "type=MBeanFactory");
// Register the naming resources
globalNamingResources.init();
// Initialize our defined Services for (Service service : services) {
service.init();
}
}
@Override protectedvoid destroyInternal() throws LifecycleException { // Destroy our defined Services for (Service service : services) {
service.destroy();
}
globalNamingResources.destroy();
unregister(onameMBeanFactory);
unregister(onameStringCache);
super.destroyInternal();
}
/** * Return the parent class loader for this component.
*/
@Override public ClassLoader getParentClassLoader() { if (parentClassLoader != null) { return parentClassLoader;
} if (catalina != null) { return catalina.getParentClassLoader();
} return ClassLoader.getSystemClassLoader();
}
/** * Set the parent class loader for this server. * * @param parent The new parent class loader
*/
@Override publicvoid setParentClassLoader(ClassLoader parent) {
ClassLoader oldParentClassLoader = this.parentClassLoader; this.parentClassLoader = parent;
support.firePropertyChange("parentClassLoader", oldParentClassLoader, this.parentClassLoader);
}
/** * Obtain the MBean domain for this server. The domain is obtained using the following search order: * <ol> * <li>Name of first {@link org.apache.catalina.Engine}.</li> * <li>Name of first {@link Service}.</li> * </ol>
*/
@Override protected String getDomainInternal() {
String domain = null;
Service[] services = findServices(); if (services.length > 0) {
Service service = services[0]; if (service != null) {
domain = service.getDomain();
}
} return domain;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.