use of jakarta.servlet.ServletContainerInitializer in project tomcat by apache.
the class ContextConfig method processServletContainerInitializers.
/**
* Scan JARs for ServletContainerInitializer implementations.
*/
protected void processServletContainerInitializers() {
List<ServletContainerInitializer> detectedScis;
try {
WebappServiceLoader<ServletContainerInitializer> loader = new WebappServiceLoader<>(context);
detectedScis = loader.load(ServletContainerInitializer.class);
} catch (IOException e) {
log.error(sm.getString("contextConfig.servletContainerInitializerFail", context.getName()), e);
ok = false;
return;
}
for (ServletContainerInitializer sci : detectedScis) {
initializerClassMap.put(sci, new HashSet<>());
HandlesTypes ht;
try {
ht = sci.getClass().getAnnotation(HandlesTypes.class);
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.info(sm.getString("contextConfig.sci.debug", sci.getClass().getName()), e);
} else {
log.info(sm.getString("contextConfig.sci.info", sci.getClass().getName()));
}
continue;
}
if (ht == null) {
continue;
}
Class<?>[] types = ht.value();
if (types == null) {
continue;
}
for (Class<?> type : types) {
if (type.isAnnotation()) {
handlesTypesAnnotations = true;
} else {
handlesTypesNonAnnotations = true;
}
Set<ServletContainerInitializer> scis = typeInitializerMap.get(type);
if (scis == null) {
scis = new HashSet<>();
typeInitializerMap.put(type, scis);
}
scis.add(sci);
}
}
}
use of jakarta.servlet.ServletContainerInitializer in project tomcat by apache.
the class ContextConfig method checkHandlesTypes.
/**
* For classes packaged with the web application, the class and each
* super class needs to be checked for a match with {@link HandlesTypes} or
* for an annotation that matches {@link HandlesTypes}.
* @param javaClass the class to check
* @param javaClassCache a class cache
*/
protected void checkHandlesTypes(JavaClass javaClass, Map<String, JavaClassCacheEntry> javaClassCache) {
// Skip this if we can
if (typeInitializerMap.size() == 0) {
return;
}
if ((javaClass.getAccessFlags() & org.apache.tomcat.util.bcel.Const.ACC_ANNOTATION) != 0) {
// Skip annotations.
return;
}
String className = javaClass.getClassName();
Class<?> clazz = null;
if (handlesTypesNonAnnotations) {
// This *might* be match for a HandlesType.
populateJavaClassCache(className, javaClass, javaClassCache);
JavaClassCacheEntry entry = javaClassCache.get(className);
if (entry.getSciSet() == null) {
try {
populateSCIsForCacheEntry(entry, javaClassCache);
} catch (StackOverflowError soe) {
throw new IllegalStateException(sm.getString("contextConfig.annotationsStackOverflow", context.getName(), classHierarchyToString(className, entry, javaClassCache)));
}
}
if (!entry.getSciSet().isEmpty()) {
// Need to try and load the class
clazz = Introspection.loadClass(context, className);
if (clazz == null) {
// Can't load the class so no point continuing
return;
}
for (ServletContainerInitializer sci : entry.getSciSet()) {
Set<Class<?>> classes = initializerClassMap.get(sci);
if (classes == null) {
classes = new HashSet<>();
initializerClassMap.put(sci, classes);
}
classes.add(clazz);
}
}
}
if (handlesTypesAnnotations) {
AnnotationEntry[] annotationEntries = javaClass.getAllAnnotationEntries();
if (annotationEntries != null) {
for (Map.Entry<Class<?>, Set<ServletContainerInitializer>> entry : typeInitializerMap.entrySet()) {
if (entry.getKey().isAnnotation()) {
String entryClassName = entry.getKey().getName();
for (AnnotationEntry annotationEntry : annotationEntries) {
if (entryClassName.equals(getClassName(annotationEntry.getAnnotationType()))) {
if (clazz == null) {
clazz = Introspection.loadClass(context, className);
if (clazz == null) {
// continuing
return;
}
}
for (ServletContainerInitializer sci : entry.getValue()) {
initializerClassMap.get(sci).add(clazz);
}
break;
}
}
}
}
}
}
}
use of jakarta.servlet.ServletContainerInitializer in project tomcat by apache.
the class ContextConfig method webConfig.
/**
* Scan the web.xml files that apply to the web application and merge them
* using the rules defined in the spec. For the global web.xml files,
* where there is duplicate configuration, the most specific level wins. ie
* an application's web.xml takes precedence over the host level or global
* web.xml file.
*/
protected void webConfig() {
/*
* Anything and everything can override the global and host defaults.
* This is implemented in two parts
* - Handle as a web fragment that gets added after everything else so
* everything else takes priority
* - Mark Servlets as overridable so SCI configuration can replace
* configuration from the defaults
*/
/*
* The rules for annotation scanning are not as clear-cut as one might
* think. Tomcat implements the following process:
* - As per SRV.1.6.2, Tomcat will scan for annotations regardless of
* which Servlet spec version is declared in web.xml. The EG has
* confirmed this is the expected behaviour.
* - As per http://java.net/jira/browse/SERVLET_SPEC-36, if the main
* web.xml is marked as metadata-complete, JARs are still processed
* for SCIs.
* - If metadata-complete=true and an absolute ordering is specified,
* JARs excluded from the ordering are also excluded from the SCI
* processing.
* - If an SCI has a @HandlesType annotation then all classes (except
* those in JARs excluded from an absolute ordering) need to be
* scanned to check if they match.
*/
WebXmlParser webXmlParser = new WebXmlParser(context.getXmlNamespaceAware(), context.getXmlValidation(), context.getXmlBlockExternal());
Set<WebXml> defaults = new HashSet<>();
defaults.add(getDefaultWebXmlFragment(webXmlParser));
Set<WebXml> tomcatWebXml = new HashSet<>();
tomcatWebXml.add(getTomcatWebXmlFragment(webXmlParser));
WebXml webXml = createWebXml();
// Parse context level web.xml
InputSource contextWebXml = getContextWebXmlSource();
if (!webXmlParser.parseWebXml(contextWebXml, webXml, false)) {
ok = false;
}
ServletContext sContext = context.getServletContext();
// Ordering is important here
// Step 1. Identify all the JARs packaged with the application and those
// provided by the container. If any of the application JARs have a
// web-fragment.xml it will be parsed at this point. web-fragment.xml
// files are ignored for container provided JARs.
Map<String, WebXml> fragments = processJarsForWebFragments(webXml, webXmlParser);
// Step 2. Order the fragments.
Set<WebXml> orderedFragments = null;
orderedFragments = WebXml.orderWebFragments(webXml, fragments, sContext);
// Step 3. Look for ServletContainerInitializer implementations
if (ok) {
processServletContainerInitializers();
}
if (!webXml.isMetadataComplete() || typeInitializerMap.size() > 0) {
// Steps 4 & 5.
processClasses(webXml, orderedFragments);
}
if (!webXml.isMetadataComplete()) {
// file.
if (ok) {
ok = webXml.merge(orderedFragments);
}
// Step 7a
// merge tomcat-web.xml
webXml.merge(tomcatWebXml);
// Step 7b. Apply global defaults
// Have to merge defaults before JSP conversion since defaults
// provide JSP servlet definition.
webXml.merge(defaults);
// Step 8. Convert explicitly mentioned jsps to servlets
if (ok) {
convertJsps(webXml);
}
// Step 9. Apply merged web.xml to Context
if (ok) {
configureContext(webXml);
}
} else {
webXml.merge(tomcatWebXml);
webXml.merge(defaults);
convertJsps(webXml);
configureContext(webXml);
}
if (context.getLogEffectiveWebXml()) {
log.info(sm.getString("contextConfig.effectiveWebXml", webXml.toXml()));
}
// Step 10. Look for static resources packaged in JARs
if (ok) {
// Spec does not define an order.
// Use ordered JARs followed by remaining JARs
Set<WebXml> resourceJars = new LinkedHashSet<>(orderedFragments);
for (WebXml fragment : fragments.values()) {
if (!resourceJars.contains(fragment)) {
resourceJars.add(fragment);
}
}
processResourceJARs(resourceJars);
// See also StandardContext.resourcesStart() for
// WEB-INF/classes/META-INF/resources configuration
}
// context
if (ok) {
for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry : initializerClassMap.entrySet()) {
if (entry.getValue().isEmpty()) {
context.addServletContainerInitializer(entry.getKey(), null);
} else {
context.addServletContainerInitializer(entry.getKey(), entry.getValue());
}
}
}
}
use of jakarta.servlet.ServletContainerInitializer in project tomcat by apache.
the class StandardContext method startInternal.
/**
* Start this component 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
protected synchronized void startInternal() throws LifecycleException {
if (log.isDebugEnabled()) {
log.debug("Starting " + getBaseName());
}
// Send j2ee.state.starting notification
if (this.getObjectName() != null) {
Notification notification = new Notification("j2ee.state.starting", this.getObjectName(), sequenceNumber.getAndIncrement());
broadcaster.sendNotification(notification);
}
setConfigured(false);
boolean ok = true;
// ensure the NamingResources follows the correct lifecycle
if (namingResources != null) {
namingResources.start();
}
// Post work directory
postWorkDirectory();
// Add missing components as necessary
if (getResources() == null) {
// (1) Required by Loader
if (log.isDebugEnabled()) {
log.debug("Configuring default Resources");
}
try {
setResources(new StandardRoot(this));
} catch (IllegalArgumentException e) {
log.error(sm.getString("standardContext.resourcesInit"), e);
ok = false;
}
}
if (ok) {
resourcesStart();
}
if (getLoader() == null) {
WebappLoader webappLoader = new WebappLoader();
webappLoader.setDelegate(getDelegate());
setLoader(webappLoader);
}
// An explicit cookie processor hasn't been specified; use the default
if (cookieProcessor == null) {
cookieProcessor = new Rfc6265CookieProcessor();
}
// Initialize character set mapper
getCharsetMapper();
// Reading the "catalina.useNaming" environment variable
String useNamingProperty = System.getProperty("catalina.useNaming");
if ((useNamingProperty != null) && (useNamingProperty.equals("false"))) {
useNaming = false;
}
if (ok && isUseNaming()) {
if (getNamingContextListener() == null) {
NamingContextListener ncl = new NamingContextListener();
ncl.setName(getNamingContextName());
ncl.setExceptionOnFailedWrite(getJndiExceptionOnFailedWrite());
addLifecycleListener(ncl);
setNamingContextListener(ncl);
}
}
// Standard container startup
if (log.isDebugEnabled()) {
log.debug("Processing standard container startup");
}
// Binding thread
ClassLoader oldCCL = bindThread();
try {
if (ok) {
// Start our subordinate components, if any
Loader loader = getLoader();
if (loader instanceof Lifecycle) {
((Lifecycle) loader).start();
}
// created.
if (loader.getClassLoader() instanceof WebappClassLoaderBase) {
WebappClassLoaderBase cl = (WebappClassLoaderBase) loader.getClassLoader();
cl.setClearReferencesRmiTargets(getClearReferencesRmiTargets());
cl.setClearReferencesStopThreads(getClearReferencesStopThreads());
cl.setClearReferencesStopTimerThreads(getClearReferencesStopTimerThreads());
cl.setClearReferencesHttpClientKeepAliveThread(getClearReferencesHttpClientKeepAliveThread());
cl.setClearReferencesObjectStreamClassCaches(getClearReferencesObjectStreamClassCaches());
cl.setClearReferencesThreadLocals(getClearReferencesThreadLocals());
}
// By calling unbindThread and bindThread in a row, we setup the
// current Thread CCL to be the webapp classloader
unbindThread(oldCCL);
oldCCL = bindThread();
// Initialize logger again. Other components might have used it
// too early, so it should be reset.
logger = null;
getLogger();
Realm realm = getRealmInternal();
if (null != realm) {
if (realm instanceof Lifecycle) {
((Lifecycle) realm).start();
}
// Place the CredentialHandler into the ServletContext so
// applications can have access to it. Wrap it in a "safe"
// handler so application's can't modify it.
CredentialHandler safeHandler = new CredentialHandler() {
@Override
public boolean matches(String inputCredentials, String storedCredentials) {
return getRealmInternal().getCredentialHandler().matches(inputCredentials, storedCredentials);
}
@Override
public String mutate(String inputCredentials) {
return getRealmInternal().getCredentialHandler().mutate(inputCredentials);
}
};
context.setAttribute(Globals.CREDENTIAL_HANDLER, safeHandler);
}
// Notify our interested LifecycleListeners
fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
// Start our child containers, if not already started
for (Container child : findChildren()) {
if (!child.getState().isAvailable()) {
child.start();
}
}
// if any
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start();
}
// Acquire clustered manager
Manager contextManager = null;
Manager manager = getManager();
if (manager == null) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("standardContext.cluster.noManager", Boolean.valueOf((getCluster() != null)), Boolean.valueOf(distributable)));
}
if ((getCluster() != null) && distributable) {
try {
contextManager = getCluster().createManager(getName());
} catch (Exception ex) {
log.error(sm.getString("standardContext.cluster.managerError"), ex);
ok = false;
}
} else {
contextManager = new StandardManager();
}
}
// Configure default manager if none was specified
if (contextManager != null) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("standardContext.manager", contextManager.getClass().getName()));
}
setManager(contextManager);
}
if (manager != null && (getCluster() != null) && distributable) {
// let the cluster know that there is a context that is distributable
// and that it has its own manager
getCluster().registerManager(manager);
}
}
if (!getConfigured()) {
log.error(sm.getString("standardContext.configurationFail"));
ok = false;
}
// We put the resources into the servlet context
if (ok) {
getServletContext().setAttribute(Globals.RESOURCES_ATTR, getResources());
if (getInstanceManager() == null) {
setInstanceManager(createInstanceManager());
}
getServletContext().setAttribute(InstanceManager.class.getName(), getInstanceManager());
InstanceManagerBindings.bind(getLoader().getClassLoader(), getInstanceManager());
// Create context attributes that will be required
getServletContext().setAttribute(JarScanner.class.getName(), getJarScanner());
// Make the version info available
getServletContext().setAttribute(Globals.WEBAPP_VERSION, getWebappVersion());
}
// Set up the context init params
mergeParameters();
// Call ServletContainerInitializers
for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry : initializers.entrySet()) {
try {
entry.getKey().onStartup(entry.getValue(), getServletContext());
} catch (ServletException e) {
log.error(sm.getString("standardContext.sciFail"), e);
ok = false;
break;
}
}
// Configure and call application event listeners
if (ok) {
if (!listenerStart()) {
log.error(sm.getString("standardContext.listenerFail"));
ok = false;
}
}
// change constraints
if (ok) {
checkConstraintsForUncoveredMethods(findConstraints());
}
try {
// Start manager
Manager manager = getManager();
if (manager instanceof Lifecycle) {
((Lifecycle) manager).start();
}
} catch (Exception e) {
log.error(sm.getString("standardContext.managerFail"), e);
ok = false;
}
// Configure and call application filters
if (ok) {
if (!filterStart()) {
log.error(sm.getString("standardContext.filterFail"));
ok = false;
}
}
// Load and initialize all "load on startup" servlets
if (ok) {
if (!loadOnStartup(findChildren())) {
log.error(sm.getString("standardContext.servletFail"));
ok = false;
}
}
// Start ContainerBackgroundProcessor thread
super.threadStart();
} finally {
// Unbinding thread
unbindThread(oldCCL);
}
// Set available status depending upon startup success
if (ok) {
if (log.isDebugEnabled()) {
log.debug("Starting completed");
}
} else {
log.error(sm.getString("standardContext.startFailed", getName()));
}
startTime = System.currentTimeMillis();
// Send j2ee.state.running notification
if (ok && (this.getObjectName() != null)) {
Notification notification = new Notification("j2ee.state.running", this.getObjectName(), sequenceNumber.getAndIncrement());
broadcaster.sendNotification(notification);
}
// The WebResources implementation caches references to JAR files. On
// some platforms these references may lock the JAR files. Since web
// application start is likely to have read from lots of JARs, trigger
// a clean-up now.
getResources().gc();
// Reinitializing if something went wrong
if (!ok) {
setState(LifecycleState.FAILED);
// Send j2ee.object.failed notification
if (this.getObjectName() != null) {
Notification notification = new Notification("j2ee.object.failed", this.getObjectName(), sequenceNumber.getAndIncrement());
broadcaster.sendNotification(notification);
}
} else {
setState(LifecycleState.STARTING);
}
}
use of jakarta.servlet.ServletContainerInitializer in project tomcat by apache.
the class TestStandardContext method testBug50015.
@Test
public void testBug50015() throws Exception {
// Test that configuring servlet security constraints programmatically
// does work.
// Set up a container
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
// Setup realm
TesterMapRealm realm = new TesterMapRealm();
realm.addUser("tomcat", "tomcat");
realm.addUserRole("tomcat", "tomcat");
ctx.setRealm(realm);
// Configure app for BASIC auth
LoginConfig lc = new LoginConfig();
lc.setAuthMethod("BASIC");
ctx.setLoginConfig(lc);
ctx.getPipeline().addValve(new BasicAuthenticator());
// Add ServletContainerInitializer
ServletContainerInitializer sci = new Bug50015SCI();
ctx.addServletContainerInitializer(sci, null);
// Start the context
tomcat.start();
// Request the first servlet
ByteChunk bc = new ByteChunk();
int rc = getUrl("http://localhost:" + getPort() + "/bug50015", bc, null);
// Check for a 401
Assert.assertNotSame("OK", bc.toString());
Assert.assertEquals(401, rc);
}
Aggregations