Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2016,7 +2016,7 @@ public ClientModule deploy(final ClientModule clientModule) throws OpenEJBExcept
buildAnnotatedRefs(client, annotationFinder, classLoader);
} catch (final ClassNotFoundException | NoClassDefFoundError e) {

logger.debug("Could not load main class {1} for client module {2} / {3}",
logger.debug("Could not load main class {0} for client module {1} / {2}",
className, clientModule.getJarLocation(), clientModule.getFile().getName());

/*
Expand All @@ -2041,7 +2041,7 @@ public ClientModule deploy(final ClientModule clientModule) throws OpenEJBExcept
clazz = classLoader.loadClass(className);
remoteClients.add(clazz);
} catch (final ClassNotFoundException | NoClassDefFoundError e) {
logger.debug("Could not load RemoteClient class {1} for client module {2} / {3}",
logger.debug("Could not load RemoteClient class {0} for client module {1} / {2}",
className, clientModule.getJarLocation(), clientModule.getFile().getName());

throw new OpenEJBException("Unable to load RemoteClient class: " + className, e);
Expand All @@ -2058,7 +2058,7 @@ public ClientModule deploy(final ClientModule clientModule) throws OpenEJBExcept
try {
clazz = classLoader.loadClass(className);
} catch (final ClassNotFoundException | NoClassDefFoundError e) {
logger.debug("Could not load LocalClient class {1} for client module {2} / {3}",
logger.debug("Could not load LocalClient class {0} for client module {1} / {2}",
className, clientModule.getJarLocation(), clientModule.getFile().getName());
throw new OpenEJBException("Unable to load LocalClient class: " + className, e);
}
Expand Down Expand Up @@ -2189,7 +2189,7 @@ public WebModule deploy(final WebModule webModule) throws OpenEJBException {
clazz = classLoader.loadClass(application);
classes.add(clazz);
} catch (final ClassNotFoundException | NoClassDefFoundError e) {
logger.debug("Could not load rest Application class {1} for module {2} / {3}",
logger.debug("Could not load rest Application class {0} for module {1} / {2}",
application, webModule.getJarLocation(), webModule.getFile().getName());
throw new OpenEJBException("Unable to load Application class: " + application, e);
}
Expand Down Expand Up @@ -2263,10 +2263,18 @@ public WebModule deploy(final WebModule webModule) throws OpenEJBException {
servlet.setServletClass(servletClass);
}
} catch (final ClassNotFoundException | NoClassDefFoundError e) {
logger.debug("Could not load Servlet class {1} for web module {2} / {3}",
logger.debug("Could not load Servlet class {0} for web module {1} / {2}",
servletClass, webModule.getJarLocation(), webModule.getFile().getName());
if (servlet.getServletClass() != null) {
throw new OpenEJBException("Unable to load servlet class: " + servletClass, e);
// The class is not available for annotation scanning, but that must not fail the
// whole context. Jakarta Servlet 6.1, section 2.3.1 "Loading and Instantiation"
// allows loading to be "delayed until the container determines the servlet is
// needed to service a request", so a servlet whose class is absent may simply
// never be used (or be registered programmatically). Let resolution happen later.
// Note this also skips @Resource/@EJB processing for that servlet, so if the
// class does resolve later it comes up without its injections.
logger.warning("Unable to load servlet class: " + servletClass + " for web module "
+ webModule.getJarLocation(), e);
} else {
logger.error("servlet " + servletName + " has no servlet-class defined and is not a subclass of Application");
}
Expand Down Expand Up @@ -2294,9 +2302,16 @@ public WebModule deploy(final WebModule webModule) throws OpenEJBException {
final Class clazz = classLoader.loadClass(filterClass);
classes.add(clazz);
} catch (final ClassNotFoundException | NoClassDefFoundError e) {
logger.debug("Could not load Servlet Filter class {1} for web module {2} / {3}",
logger.debug("Could not load Servlet Filter class {0} for web module {1} / {2}",
filterClass, webModule.getJarLocation(), webModule.getFile().getName());
throw new OpenEJBException("Unable to load servlet filter class: " + filterClass, e);
// Jakarta Servlet 6.1 section 6.2.1 requires the filter to be instantiated before a
// request reaches a resource it is mapped to - i.e. later than this, but unlike a
// servlet it cannot be skipped if it is ever used. So a missing class is very likely a
// real packaging error; log it at error level, but keep it local to the filter rather
// than failing the whole application (the spec's "must fail to deploy" rules cover
// web-fragment ordering conflicts, not unresolvable component classes).
logger.error("Unable to load servlet filter class: " + filterClass + " for web module "
+ webModule.getJarLocation(), e);
}
}
}
Expand All @@ -2311,9 +2326,13 @@ public WebModule deploy(final WebModule webModule) throws OpenEJBException {
final Class clazz = classLoader.loadClass(listenerClass);
classes.add(clazz);
} catch (final ClassNotFoundException | NoClassDefFoundError e) {
logger.debug("Could not load Servlet listener class {1} for web module {2} / {3}",
logger.debug("Could not load Servlet listener class {0} for web module {1} / {2}",
listenerClass, webModule.getJarLocation(), webModule.getFile().getName());
throw new OpenEJBException("Unable to load servlet listener class: " + listenerClass, e);
// A declared listener is instantiated at application startup, so - as for filters
// above - a missing class is very likely a real packaging error rather than something
// that can be deferred. Report it at error level but keep the failure local.
logger.error("Unable to load servlet listener class: " + listenerClass + " for web module "
+ webModule.getJarLocation(), e);
}
}
}
Expand All @@ -2329,7 +2348,7 @@ public WebModule deploy(final WebModule webModule) throws OpenEJBException {
final Class clazz = classLoader.loadClass(listenerClass);
classes.add(clazz);
} catch (final ClassNotFoundException | NoClassDefFoundError e) {
logger.debug("Could not load TagLib listener class {1} for web module {2} / {3}",
logger.debug("Could not load TagLib listener class {0} for web module {1} / {2}",
listenerClass, webModule.getJarLocation(), webModule.getFile().getName());
logger.error("Unable to load tag library servlet listener class: " + listenerClass);
}
Expand All @@ -2346,7 +2365,7 @@ public WebModule deploy(final WebModule webModule) throws OpenEJBException {
final Class clazz = classLoader.loadClass(tagClass);
classes.add(clazz);
} catch (final ClassNotFoundException | NoClassDefFoundError e) {
logger.debug("Could not load tag class {1} for web module {2} / {3}",
logger.debug("Could not load tag class {0} for web module {1} / {2}",
tagClass, webModule.getJarLocation(), webModule.getFile().getName());
logger.error("Unable to load tag library tag class: " + tagClass);
}
Expand Down Expand Up @@ -2376,8 +2395,12 @@ public WebModule deploy(final WebModule webModule) throws OpenEJBException {
final Class clazz = classLoader.loadClass(handlerClass);
classes.add(clazz);
} catch (final ClassNotFoundException | NoClassDefFoundError e) {
logger.debug("Could not load web service handler class {1} for web module {2} / {3}",
logger.debug("Could not load web service handler class {0} for web module {1} / {2}",
handlerClass, webModule.getJarLocation(), webModule.getFile().getName());
// Deliberately still fatal, unlike the servlet/filter/listener sites above:
// a handler chain silently missing a handler would leave the endpoint
// running with a different (weaker) contract than declared, e.g. dropping
// a security or logging handler, rather than just disabling one component.
throw new OpenEJBException("Unable to load webservice handler class: " + handlerClass, e);
}
}
Expand Down Expand Up @@ -2926,7 +2949,7 @@ public EjbModule deploy(final EjbModule ejbModule) throws OpenEJBException {
try {
clazz = classLoader.loadClass(realClassName(interceptor.getInterceptorClass()));
} catch (final ClassNotFoundException e) {
logger.debug("Could not load interceptor class {1} for enterprise beans module {2} / {3}",
logger.debug("Could not load interceptor class {0} for enterprise beans module {1} / {2}",
interceptor.getInterceptorClass(), ejbModule.getJarLocation(), ejbModule.getFile().getName());

throw new OpenEJBException("Unable to load interceptor class: " + interceptor.getInterceptorClass(), e);
Expand Down Expand Up @@ -5693,7 +5716,7 @@ private static void addRestClassesToScannedClasses(final WebModule webModule, fi
clazz = classLoader.loadClass(className);
classes.add(clazz);
} catch (final ClassNotFoundException e) {
logger.debug("Could not load REST class {1} for web module {2} / {3}",
logger.debug("Could not load REST class {0} for web module {1} / {2}",
className, webModule.getJarLocation(), webModule.getFile().getName());

throw new OpenEJBException("Unable to load REST class: " + className, e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,19 @@ private void processPorts(final WebModule webModule) throws OpenEJBException {
continue;
}

// TOMEE-4642: this runs for every servlet, not just webservice endpoints, so a servlet
// class the war does not package must not fail the whole deployment here either.
// NoClassDefFoundError is an Error, so the catch below would not cover it.
final Class<?> clazz;
try {
clazz = webModule.getClassLoader().loadClass(className);
} catch (final ClassNotFoundException | NoClassDefFoundError e) {
logger.warning("Unable to load servlet class: " + className + " for web module "
+ webModule.getJarLocation() + ", skipping webservice detection for it", e);
continue;
}

try {
final Class<?> clazz = webModule.getClassLoader().loadClass(className);
if (JaxWsUtils.isWebService(clazz)) {
// add servlet mapping if not already declared
ServletMapping servletMapping = servletMappings.get(servlet.getServletName());
Expand Down Expand Up @@ -231,7 +242,8 @@ private void processPorts(final WebModule webModule) throws OpenEJBException {
}
}
} catch (final Exception e) {
throw new OpenEJBException("Unable to load servlet class: " + className, e);
// the class resolved fine, so this is a genuine webservice configuration problem
throw new OpenEJBException("Unable to configure webservice for servlet class: " + className, e);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,16 @@ public void deployWebApps(final AppInfo appInfo, final ClassLoader appClassLoade

// listeners
for (final ListenerInfo listener : webAppInfo.listeners) {
final Class<?> clazz = webContext.getClassLoader().loadClass(listener.classname);
final Class<?> clazz;
try {
clazz = webContext.getClassLoader().loadClass(listener.classname);
} catch (final ClassNotFoundException | NoClassDefFoundError e) {
// TOMEE-4642: a listener class the war does not package is reported by the
// deployer and must not abort the rest of the application here either.
LOGGER.error("Unable to load listener class: " + listener.classname
+ " for web application " + webAppInfo.contextRoot, e);
continue;
}
final Object instance = webContext.newInstance(clazz);
if (ServletContextListener.class.isInstance(instance)) {
switchServletContextIfNeeded(sce.getServletContext(), new Runnable() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,15 @@
import org.apache.openejb.assembler.classic.AppInfo;
import org.apache.openejb.assembler.classic.Assembler;
import org.apache.openejb.assembler.classic.ClientInfo;
import org.apache.openejb.assembler.classic.WebAppInfo;
import org.apache.openejb.jee.AssemblyDescriptor;
import org.apache.openejb.jee.ConfigProperty;
import org.apache.openejb.jee.Connector;
import org.apache.openejb.jee.EjbJar;
import org.apache.openejb.jee.EnterpriseBean;
import org.apache.openejb.jee.Filter;
import org.apache.openejb.jee.Listener;
import org.apache.openejb.jee.Servlet;
import org.apache.openejb.jee.SessionBean;
import org.apache.openejb.jee.TransactionSupportType;
import org.apache.openejb.jee.WebApp;
Expand Down Expand Up @@ -82,6 +86,7 @@
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;

Expand Down Expand Up @@ -496,6 +501,82 @@ public void setMyNumber(final int myNumber) {
}
}

/**
* TOMEE-4642: a servlet, filter or listener class named in web.xml but not packaged in the war
* must not abort the deployment of the whole web module.
*/
@Test
public void missingServletFilterAndListenerClassesDoNotFailDeployment() throws Exception {
final WebApp webApp = new WebApp();
webApp.setContextRoot("/");
webApp.setId("web");
webApp.setVersion("2.5");

final Servlet servlet = new Servlet();
servlet.setServletName("TestServlet1");
servlet.setServletClass("org.apache.openejb.config.missing.TestServlet1");
webApp.getServlet().add(servlet);

final Filter filter = new Filter();
filter.setFilterName("AddFilterString");
filter.setFilterClass("org.apache.openejb.config.missing.AddFilterString");
webApp.getFilter().add(filter);

final Listener listener = new Listener();
listener.setListenerClass("org.apache.openejb.config.missing.MissingListener");
webApp.getListener().add(listener);

WebModule webModule = new WebModule(webApp, webApp.getContextRoot(), Thread.currentThread().getContextClassLoader(), "myapp", webApp.getId());
webModule.setFinder(new AnnotationFinder(new ClassesArchive()).link());

final AnnotationDeployer annotationDeployer = new AnnotationDeployer();
webModule = annotationDeployer.deploy(webModule);

// the declarations are kept as-is, they are simply not resolved for annotation scanning
assertEquals(1, webModule.getWebApp().getServlet().size());
assertEquals(1, webModule.getWebApp().getFilter().size());
assertEquals(1, webModule.getWebApp().getListener().size());
}

/**
* TOMEE-4642: same as above, but driven through the whole deployer chain rather than
* AnnotationDeployer alone. WsDeployer is part of that chain whenever wsdl4j is on the
* classpath (Plus, Plume, openejb-standalone) and loads every servlet class before testing
* whether it is a webservice, so it used to fail the deployment on those distributions even
* with AnnotationDeployer fixed.
*/
@Test
public void missingServletClassDoesNotFailFullConfiguration() throws Exception {
final WebApp webApp = new WebApp();
webApp.setContextRoot("/");
webApp.setId("web");
webApp.setVersion("2.5");

final Servlet servlet = new Servlet();
servlet.setServletName("TestServlet1");
servlet.setServletClass("org.apache.openejb.config.missing.TestServlet1");
webApp.getServlet().add(servlet);

final Filter filter = new Filter();
filter.setFilterName("AddFilterString");
filter.setFilterClass("org.apache.openejb.config.missing.AddFilterString");
webApp.getFilter().add(filter);

final Listener listener = new Listener();
listener.setListenerClass("org.apache.openejb.config.missing.MissingListener");
webApp.getListener().add(listener);

final WebModule webModule = new WebModule(webApp, webApp.getContextRoot(),
Thread.currentThread().getContextClassLoader(), "myapp", webApp.getId());
webModule.setFinder(new AnnotationFinder(new ClassesArchive()).link());

final WebAppInfo webAppInfo = new ConfigurationFactory().configureApplication(webModule);

assertNotNull(webAppInfo);
assertEquals(1, webAppInfo.servlets.size());
assertEquals("TestServlet1", webAppInfo.servlets.iterator().next().servletName);
}

@Test
public void findRestClasses() throws Exception {
final WebApp webApp = new WebApp();
Expand Down
Loading