GUACAMOLE-1224: Add events for overall webapp startup/shutdown.

This commit is contained in:
Michael Jumper
2022-10-02 11:19:01 -07:00
parent 9bda1b2c19
commit 8c36eaf55d
4 changed files with 115 additions and 4 deletions

View File

@@ -0,0 +1,36 @@
/*
* 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.guacamole.net.event;
import org.apache.guacamole.net.auth.AuthenticationProvider;
import org.apache.guacamole.net.auth.UserContext;
import org.apache.guacamole.net.event.listener.Listener;
/**
* Event that is dispatched when the web application has nearly completely shut
* down, including the authentication/authorization portion of extensions. Any
* installed extensions are still loaded (such that they may receive this event
* via {@link Listener#handleEvent(java.lang.Object)}, but their authentication
* providers will have been shut down via {@link AuthenticationProvider#shutdown()},
* and resources from user sessions will have been closed and released via
* {@link UserContext#invalidate()}.
*/
public interface ApplicationShutdownEvent {
}

View File

@@ -0,0 +1,29 @@
/*
* 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.guacamole.net.event;
/**
* Event that is dispatched when the web application has finished starting up,
* including all extensions. This event indicates only that the web application
* startup process has completed and all extensions have been loaded. It does
* not indicate whether all extensions have started successfully.
*/
public interface ApplicationStartedEvent {
}

View File

@@ -35,11 +35,14 @@ import org.apache.guacamole.environment.LocalEnvironment;
import org.apache.guacamole.extension.ExtensionModule; import org.apache.guacamole.extension.ExtensionModule;
import org.apache.guacamole.log.LogModule; import org.apache.guacamole.log.LogModule;
import org.apache.guacamole.net.auth.AuthenticationProvider; import org.apache.guacamole.net.auth.AuthenticationProvider;
import org.apache.guacamole.net.event.ApplicationShutdownEvent;
import org.apache.guacamole.net.event.ApplicationStartedEvent;
import org.apache.guacamole.properties.BooleanGuacamoleProperty; import org.apache.guacamole.properties.BooleanGuacamoleProperty;
import org.apache.guacamole.properties.FileGuacamoleProperties; import org.apache.guacamole.properties.FileGuacamoleProperties;
import org.apache.guacamole.rest.RESTServiceModule; import org.apache.guacamole.rest.RESTServiceModule;
import org.apache.guacamole.rest.auth.HashTokenSessionMap; import org.apache.guacamole.rest.auth.HashTokenSessionMap;
import org.apache.guacamole.rest.auth.TokenSessionMap; import org.apache.guacamole.rest.auth.TokenSessionMap;
import org.apache.guacamole.rest.event.ListenerService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -126,6 +129,12 @@ public class GuacamoleServletContextListener extends GuiceServletContextListener
@Inject @Inject
private List<File> temporaryFiles; private List<File> temporaryFiles;
/**
* Service for dispatching events to registered listeners.
*/
@Inject
private ListenerService listenerService;
/** /**
* Internal reference to the Guice injector that was lazily created when * Internal reference to the Guice injector that was lazily created when
* getInjector() was first invoked. * getInjector() was first invoked.
@@ -179,6 +188,17 @@ public class GuacamoleServletContextListener extends GuiceServletContextListener
// Store reference to injector for use by Jersey and HK2 bridge // Store reference to injector for use by Jersey and HK2 bridge
servletContextEvent.getServletContext().setAttribute(GUICE_INJECTOR, injector); servletContextEvent.getServletContext().setAttribute(GUICE_INJECTOR, injector);
// Inform any listeners that application startup has completed
try {
listenerService.handleEvent(new ApplicationStartedEvent() {
// The application startup event currently has no content
});
}
catch (GuacamoleException e) {
logger.error("An extension listening for application startup failed: {}", e.getMessage());
logger.debug("Extension failed internally while handling the application startup event.", e);
}
} }
@Override @Override
@@ -228,19 +248,36 @@ public class GuacamoleServletContextListener extends GuiceServletContextListener
// Clean up reference to Guice injector // Clean up reference to Guice injector
servletContextEvent.getServletContext().removeAttribute(GUICE_INJECTOR); servletContextEvent.getServletContext().removeAttribute(GUICE_INJECTOR);
// Shutdown TokenSessionMap // Shutdown TokenSessionMap, invalidating all sessions (logging all
// users out)
if (sessionMap != null) if (sessionMap != null)
sessionMap.shutdown(); sessionMap.shutdown();
// Unload all extensions // Unload authentication for all extensions
if (authProviders != null) { if (authProviders != null) {
for (AuthenticationProvider authProvider : authProviders) for (AuthenticationProvider authProvider : authProviders)
authProvider.shutdown(); authProvider.shutdown();
} }
// Inform any listeners that application shutdown has completed
try {
listenerService.handleEvent(new ApplicationShutdownEvent() {
// The application shutdown event currently has no content
});
}
catch (GuacamoleException e) {
logger.error("An extension listening for application shutdown failed: {}", e.getMessage());
logger.debug("Extension failed internally while handling the application shutdown event.", e);
}
} }
finally { finally {
// NOTE: This temporary file cleanup must happen AFTER firing the
// ApplicationShutdownEvent, or an extension that relies on a .jar
// file among those temporary files might fail internally when
// attempting to process the event.
// Regardless of what may succeed/fail here, always attempt to // Regardless of what may succeed/fail here, always attempt to
// clean up ALL temporary files // clean up ALL temporary files
if (temporaryFiles != null) if (temporaryFiles != null)

View File

@@ -22,6 +22,8 @@ package org.apache.guacamole.event;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleResourceNotFoundException; import org.apache.guacamole.GuacamoleResourceNotFoundException;
import org.apache.guacamole.net.event.ApplicationShutdownEvent;
import org.apache.guacamole.net.event.ApplicationStartedEvent;
import org.apache.guacamole.net.event.DirectoryEvent; import org.apache.guacamole.net.event.DirectoryEvent;
import org.apache.guacamole.net.event.DirectoryFailureEvent; import org.apache.guacamole.net.event.DirectoryFailureEvent;
import org.apache.guacamole.net.event.DirectorySuccessEvent; import org.apache.guacamole.net.event.DirectorySuccessEvent;
@@ -115,12 +117,19 @@ public class EventLoggingListener implements Listener {
@Override @Override
public void handleEvent(@Nonnull Object event) throws GuacamoleException { public void handleEvent(@Nonnull Object event) throws GuacamoleException {
// General object creation/modification/deletion
if (event instanceof DirectorySuccessEvent) if (event instanceof DirectorySuccessEvent)
logSuccess((DirectorySuccessEvent<?>) event); logSuccess((DirectorySuccessEvent<?>) event);
else if (event instanceof DirectoryFailureEvent) else if (event instanceof DirectoryFailureEvent)
logFailure((DirectoryFailureEvent<?>) event); logFailure((DirectoryFailureEvent<?>) event);
// Application startup/shutdown
else if (event instanceof ApplicationStartedEvent)
logger.info("The Apache Guacamole web application has started.");
else if (event instanceof ApplicationShutdownEvent)
logger.info("The Apache Guacamole web application has shut down.");
// Unknown events
else else
logger.debug("Ignoring unknown/unimplemented event type: {}", logger.debug("Ignoring unknown/unimplemented event type: {}",
event.getClass()); event.getClass());