Embedded servlet container does not handle META-INF/resources in Spring Boot - spring-boot

My WAR application use non-Spring library (for JSF). The library initializes using servletContext.getResource("page.html"). page.html is within a JAR in WEB-INF/lib, packaged as META-INF/resources/page.html
That works excellent when I deploy WAR on servlet container. But when I run application as executable WAR it does not work because embedded servlet container does not scan classpath META-INF/resources.
For example for Undertow classpath resource manager is not used:
private ResourceManager getDocumentRootResourceManager() {
File root = getCanonicalDocumentRoot();
if (root.isDirectory()) {
return new FileResourceManager(root, 0);
}
if (root.isFile()) {
return new JarResourceManager(root);
}
return ResourceManager.EMPTY_RESOURCE_MANAGER;
}
https://github.com/spring-projects/spring-boot/blob/master/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactory.java#L466
Time for question: why embedded servlet container ignores META-INF/resources? It is a problem to make executable Servlet 3.0 applications.
Similar issues:
Embedded Tomcat, executable jar, ServletContext.getRealPath()
https://github.com/spring-projects/spring-boot/issues/4218

I solved my problem with the following configuration:
#Bean
public UndertowEmbeddedServletContainerFactory embeddedServletContainerFactory() {
UndertowEmbeddedServletContainerFactory factory =
new UndertowEmbeddedServletContainerFactory();
factory.addDeploymentInfoCustomizers(new UndertowDeploymentInfoCustomizer() {
#Override
public void customize(DeploymentInfo deploymentInfo) {
deploymentInfo.setResourceManager(
new ClassPathResourceManager(deploymentInfo.getClassLoader(),
"META-INF/resources"));
});
return factory;
}
Generally, I think that it is underdocumented how embedded web container behaves. Spring Boot developers are happy if embdedded web container offers some subset of features to application, people who migrate existing application expects embedded container provides all features of regular container. For example ServletContainerInitializers are ignored in embedded: https://github.com/spring-projects/spring-boot/issues/321

Related

GORM Data Services are not injected when deployed to Tomcat

Why are GORM Data Services are not injected into ordinary Grails services when deployed to Tomcat?
We have a couple of domain/service/dataservice sets like the following:
//grails-app/services
class BeerService {
BeerDataService beerDataService
List<BeerEntity> list() {
beerDataService.list() << throws NPE
}
}
//grails-app/services
#grails.gorm.services.Service(BeerEntity)
interface BeerDataService {
List<BeerEntity> list()
}
//grails-app/domain
class BeerEntity {
String type
}
This works as expected using bootRun or as a runnable jar (in any environment), but when deployed in Tomcat (8 or 9) the data services are not registered as beans (aka. not present in applicationContext).
Using them therefore results in NPE’s.
Fetching them from the applicationContext directly (via #Autowired or Holders) results in NoSuchBeanDefinitionException being thrown.
I’ve verified the problem in a sample project (using generate-all) deployed to a local Tomcat 8 using different Java 8 versions.
Data Services are not injected when running in Tomcat, neither in controllers nor other services.
Any thoughts on why?
I’m completely lost!
Environment:
Java: 8.0.322-tem and 8.0.302-open
Tomcat: 9.0.31 and 8.5.78
OS: Ubuntu 20.04
From gradle.properties:
grailsVersion=5.1.7
groovyVersion=3.0.7
gorm.version=7.2.1

Mapping conflict when deploying with spring boot Jersey starter deployed as WAR

We're using Spring Boot with its Jersey Starter and deploy it as a WAR, programmatically deployed into another application's embedded Tomcat.
After our application startup, in some environments, a mapping conflict occurs and is logged as follows:
o.g.j.s.i.JerseyServletContainerInitializer : Mapping conflict. A Servlet registration exists with same mapping as the Jersey servlet application, named com.vidal.pmsi.config.PmsiResourceConfiguration, at the servlet mapping, /*.
The resource configuration is as follows:
#ApplicationPath("/")
#ExposedApplication
#Component
public class PmsiResourceConfiguration extends ResourceConfig {
public PmsiResourceConfiguration() {
packages("com.vidal.pmsi.api");
packages("com.vidal.pmsi.config");
property(ServerProperties.BV_DISABLE_VALIDATE_ON_EXECUTABLE_OVERRIDE_CHECK, true);
property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
}
}
As far as I understand, Spring Boot Jersey Starter will register a 'jerseyServlet'-named servlet mapping to '/*'.
In some environments, Jersey's own JerseyServletContainerInitializer will trigger after SpringApplication startup, failing to register PmsiResourceConfiguration because of the existing jerseyServlet mapping.
This is a problem because of our own open-source library that tries (and crashes at) getting the context path at startup:
// compile-time generated Linkers.java
#WebListener
#Generated("fr.vidal.oss.jax_rs_linker.LinkerAnnotationProcessor")
public final class Linkers implements ServletContextListener {
private static String contextPath = "";
private static String applicationName = ApplicationName.get();
#Override
public void contextInitialized(ServletContextEvent sce) {
//applicationName = FQCN of PmsiResourceConfiguration
contextPath = ContextPaths.contextPath(sce.getServletContext(), applicationName);
}
// [...]
}
// ContextPaths.java
public static String contextPath(ServletContext servletContext, String registeredKey) {
// registeredKey is therefore the FQCN of PmsiResourceConfiguration
String mappedPath = stripWildcard(servletContext.getServletRegistration(registeredKey).getMappings().iterator().next());
return servletContext.getContextPath() + mappedPath;
}
The last snippet of code will fail as there is no mapping for the registered resource configuration class (there is only one for 'jerseyServlet' key).
This does not fail when there isn't any mapping conflict reported.
Why?
I was running my head against a similar problem where I have a Spring Boot Application with a Jersey JAX-RS Webservice. Everything worked fine when using the embeddedTomcat but it went to hell when I tried deploying the war on a regular Tomcat of the same version (Tomcat8).
The problem is that by default the embeddedTomcat does not scan for ServletContainerInitializer in jar files, yet the regular one does and that conflicted with the ServletContainer/Config that Spring sets up.
Apart from excluding the jar containing the JerseyServletContainerInitializer I found an option to tell tomcat to filter out this particular ServletContainerInitializer (SCI). Setting the containerSciFilter attribute on the context helped:
<Context containerSciFilter="JerseyServletContainerInitializer">
...
</Context>
I did not define any SCI in my META-INF/services but the jar that contains the JerseySCI has defined it and it was on the right path to be found by Tomcat.
Considering that this was the closest matchinb question and without an answer I don't repost my question and try to answer this one as I believe that the cause is the same.

JerseyTest and Spring and creating a ServletContext

I'm working on migrating from Jersey 1.16 to Jersey 2.7. I have the application running and working, but I'm having trouble with the tests.
Some things to note:
The application uses Spring and is configured by a class, ApplicationConfiguration.class.
The application does not have a web.xml - the Jersey servlet and filters are configured programmatically.
I am using the jersey-spring3 library.
Related to using the jersey-spring3 library, I have to add a workaround to my onStartup method
// The following line is required to avoid having jersey-spring3 registering it's own Spring root context.
// https://java.net/jira/browse/JERSEY-2038
servletContext.setInitParameter("contextConfigLocation", "");
Here's the issue:
When the test is starting up, SpringComponentProvider, tries to initialize Spring with a dangerous assumption that I can't figure out how to correct - xml based configuration. Looking at the code, the trouble is this block
ServletContext sc = locator.getService(ServletContext.class);
if(sc != null) {
// servlet container
ctx = WebApplicationContextUtils.getWebApplicationContext(sc);
} else {
// non-servlet container
ctx = createSpringContext();
}
Running a JUnit test, ServletContext is null, and createSpringContext is called.
Here's the question:
Is there a way to run a test and specify a ServletContext/ServletContainer?
I believe this issue is covered by https://java.net/jira/browse/JERSEY-2259.
In short: they removed this functionality from Jersey 2.x and are treating it as a Feature Request (instead of regression) so it's not considered a high-priority item.

WebApplicationInitializer being called repeatedly

I have a Spring 3.1 based application hosted under Tomcat 7.x (latest version). The application is configured exclusively with Java (no web.xml, no Spring XML configuration). All unit tests are passing, including ones using the Spring Java configuration (#ContextConfiguration).
The problem is that when the application is deployed, the WebApplicationInitializer implementation is being called multiple times. Repeated registrations of filters and listeners causes exceptions and the application never starts.
I was not expecting WebApplicationInitializer.onStartup() to be called repeatedly and would like to eliminate that behavior if possible. If anyone has suggestions on why this might be happening, and how to stop it, I'd really appreciate it.
Update I believe the problem is external to the initialization class itself, but here it is in case I am mistaken...
public class DeploymentDescriptor implements WebApplicationInitializer {
private static final Logger LOGGER = LoggerFactory.getLogger("org.ghc.web-app-initializer");
#Override
public void onStartup (ServletContext servletContext) throws ServletException {
// This is the programmatic way of declaring filters. This allows you to order
// Filters. The order of these security filters DOES MATTER!
FilterRegistration.Dynamic mockSecurityFilter = servletContext.addFilter ("mockSecurityFilter", "org.ghc.security.MockSecurityFilter");
mockSecurityFilter.addMappingForUrlPatterns (EnumSet.of (REQUEST), true, "/*");
FilterRegistration.Dynamic siteMinderSecurityFilter = servletContext.addFilter ("siteMinderSecurityFilter", "org.ghc.security.SiteMinderSecurityFilter");
siteMinderSecurityFilter.addMappingForUrlPatterns (EnumSet.of (REQUEST), true, "/*");
FilterRegistration.Dynamic userDetailsStoreFilter = servletContext.addFilter ("userDetailsStoreFilter", "org.ghc.security.UserDetailsStoreFilter");
userDetailsStoreFilter.addMappingForUrlPatterns (EnumSet.of (REQUEST), true, "/*");
// Static resource handling using "default" servlet
servletContext.getServletRegistration ("default").addMapping ("*.js", "*.css", "*.jpg", "*.gif", "*.png");
// Map jspf files to jsp servlet
servletContext.getServletRegistration ("jsp").addMapping ("*.jspf");
// Spin up the Spring 3.1 class that can scan a package tree for classes
// annotated with #Configuration. See org.ghc.spring3.ControllerConfiguration for
// this example.
final AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext ();
dispatcherContext.setServletContext (servletContext);
dispatcherContext.register(ScProviderDirectory.class);
dispatcherContext.refresh ();
// Spin up the Spring DispatcherServlet (just like before) passing the just built
// application context. Load it like the regular Servlet that it is!
final ServletRegistration.Dynamic servlet = servletContext.addServlet ("spring", new DispatcherServlet(dispatcherContext));
servlet.setLoadOnStartup (1);
servlet.addMapping ("/"); // Make sure this is NOT "/*"!
}
}
Update 2 This is just weird. The Tomcat logs appear to identify two instances of my DeploymentDescriptor class. I verified that there is only one instance of this class in my .war file though. I have no idea where the second (phantom) instance is coming from, but at least this explains why the class is being scanned twice...
logs/localhost.2012-10-09.log:INFO: Spring WebApplicationInitializers detected on classpath: [org.ghc.configuration.DeploymentDescriptor#3b29642c]
logs/localhost.2012-10-09.log:INFO: Spring WebApplicationInitializers detected on classpath: [org.ghc.configuration.DeploymentDescriptor#432c4c7a]
The problem here was a Maven Overlay dumping crap a Spring xml configuration file into my application. For whatever reason this caused the WebApplicationInitializer.onStartup() to be called twice. Probably an initialization for the application context, and for the servlet context. Killed off the overlay, application is initializing as expected.
I had the same problem. The issue was that I had multiple spring-web*.jar like Biju K. suggested (one as part of the war, the other in shared/tomcat library).
I had the same problem. Running mvn clean in the web app module's directory and then starting up Tomcat solved it for me.

Using Spring to Access an EJB Across Clusters in WebSphere using Grails

I have spent the last few days attempting to integrate a Grails (version 1.3.2) application with an EJB 2.1 application that is deployed on WebSphere 6.1. Once our grails apps are in production, they will be deployed to WebSphere as well. The EJB 2.1 application is widely used across our company and, in anything except a local development environment, is deployed to its own cluster. The way we handle this in our existing Java EE applications (all of which are non-Spring, non-Grails) is to bind a CORBA CosNaming Naming Context within each of our other clusters that can then be used to obtain references to our shared EJB 2.1 application. So, up to this point, if one of our application needed to interact with this application, they would do so using an approach like this:
String cosNameBinding = "ejbApp.HighAvail.cluster";
InitialContext initial = new InitialContext();
Context fedContext = (javax.naming.Context) initialCtx.lookup(cosNameBinding);
Then do the normal EJB-style lookup/narrow/invoke using the federated/CosNaming context:
Object ejbHomeAsObject = fedContext.lookup(jndiNameOfService);
EJBHome home = (EJBHome) PortableRemoteObject.narrow(ejbHomeAsObject, homeClass);
Object service = invokeMethod(homeClass, home, "create");
As you can see, there is a level of indirection that occurs here in order to go from the InitialContext to the federated naming Context that can be used to interact with the shared EJB application.
Running locally, I have both the Grails application and the EJB application deployed to the same server (non network deployment WAS, same profile&node). I have Spring configured like so:
beans = {
ejbJndi(org.springframework.jndi.JndiTemplate) {
environment = ["java.naming.factory.initial" :
"com.ibm.websphere.naming.WsnInitialContextFactory"]
}
crewMemberService(org.springframework.ejb.access.SimpleRemoteStatelessSessionProxyFactoryBean) {
jndiName="hotelService/ejb/HotelService"
businessInterface="com.company.appName.hotel.HotelService"
lookupHomeOnStartup="false"
cacheHome="false"
refreshHomeOnConnectFailure="true"
jndiTemplate = ref("ejbJndi")
}
}
And I can successfully inject ejb references into my Grails controllers and invoke them. However, WebSphere can only resolve the JNDI lookup because they are both deployed on the same server. When we move it to one of our development environments, we'll need jndi lookups for these services to go against the federated naming context.
So my questions are:
Is there a way to do this with the classes that are provided within Spring and if so could you give me an idea of how I would need up modify my Spring config to do so?
Given that there is no flexibility around how we deploy the other app or gain references to its services (we must use the federated context), should I consider extending JndiTemplate and do the necessary wiring myself?
If anyone has faced this situation I would be most appreciative for any insights you may be able to offer.
In case anyone has this same question down the road, I ended up implementing an extension to Spring's JndiTemplate and using that. Here is the code:
public class FederatedJndiTemplate extends org.springframework.jndi.JndiTemplate
{
protected static final String JNDI_CONTEXT_BINDING_NAME = "fed.context.jndiName";
/**
* Obtain a JNDI naming context for the specified federated naming context.
*
* #throws NamingException if no "fed.context.jndiName" has been specified in
* the environment properties for the jndiTemplate or the container throws a naming
* exception.
*/
#Override
protected Context createInitialContext() throws NamingException {
Properties props = super.getEnvironment();
if(!props.containsKey(JNDI_CONTEXT_BINDING_NAME)) {
throw new NamingException("You must specify the federated naming context JNDI binding name");
}
String jndiBinding = props.getProperty(JNDI_CONTEXT_BINDING_NAME);
InitialContext initCtx = new InitialContext();
Context fedCtx = (Context) initCtx.lookup(jndiBinding);
return fedCtx;
}
}
Then inside my resources.groovy, I just used this JndiTemplate:
ejbJndi(com.myCompany.spring.jndi.FederatedJndiTemplate) {
environment = [
"fed.context.jndiName":"myServices.HighAvail.Cluster"]
}
hotelService(org.springframework.ejb.access.SimpleRemoteStatelessSessionProxyFactoryBean) {
jndiName="hotelService/ejb/HotelService"
businessInterface="com.mycompany.appName.hotel.HotelService"
homeInterface="com.mycompany.appName.hotel.HotelServiceHome"
lookupHomeOnStartup="false"
jndiTemplate = ref("ejbJndi")
}

Resources