Embedded Tomcat with Spring throws IllegalAccessError: <proxy> cannot access its superinterface <class> - spring

I have an application that works just fine as war in tomcat. Now I would like to start it from Embedded Tomcat using such code:
public static void main(String[] args) throws Exception {
Tomcat tomcat = new Tomcat();
tomcat.enableNaming();
StandardContext ctx =
(StandardContext) tomcat.addWebapp("/myapp", new File("src/main/webapp/").getAbsolutePath());
StandardJarScanner jarScanner = (StandardJarScanner) ctx.getJarScanner();
jarScanner.setScanClassPath(false);
jarScanner.setScanAllDirectories(false);
jarScanner.setScanBootstrapClassPath(false);
tomcat.start();
tomcat.getServer().await();
}
Application seams to be starting but after loading of few Spring Services it gets such Exception
Caused by: java.lang.IllegalAccessError: class de.abc.service.intern.$Proxy213 cannot access its superinterface de.abc.service.intern.PageMetadataStorage
at java.lang.reflect.Proxy.defineClass0(Native Method)
at java.lang.reflect.Proxy.access$300(Proxy.java:228)
at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:642)
at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:557)
at java.lang.reflect.WeakCache$Factory.get(WeakCache.java:230)
at java.lang.reflect.WeakCache.get(WeakCache.java:127)
at java.lang.reflect.Proxy.getProxyClass0(Proxy.java:419)
at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:719)
at org.springframework.aop.framework.JdkDynamicAopProxy.getProxy(JdkDynamicAopProxy.java:122)
at org.springframework.aop.framework.ProxyFactory.getProxy(ProxyFactory.java:109)
Looks like $Proxy213 got loaded by bootstrap class loader and cannot access class from web app. And problematic interface is package protected.
Could some one explain what is happening here? Could I solve it somehow?

Related

How to disable SpringBoot autoconfiguration for TomcatServletWebServerFactory in order for a custom spring-starter to provide it?

so I was writing my own SpringBootStarter which was supposed to enable the JNDI lookup in the embedded tomcat of a SpringBoot application.
My sample SpringBoot application has a dependency of my custom SpringBootStarter, which in turn has a dependency on the spring-boot-starter-web. If I create a Configuration class like the following inside the sample SpringBoot application everything works perfectly:
#Configuration
public class SampleSpringBootAppConfig {
#Bean
public TomcatServletWebServerFactory tomcatFactory() {
return new TomcatServletWebServerFactory() {
#Override
protected TomcatWebServer getTomcatWebServer(org.apache.catalina.startup.Tomcat tomcat) {
System.out.println("CONFIGURING CUSTOM TOMCAT WEB SERVER FACTORY ");
tomcat.enableNaming();
return super.getTomcatWebServer(tomcat);
}
#Override
protected void postProcessContext(Context context) {
ContextResource resource = new ContextResource();
resource.setName("myDataSource");
resource.setType(DataSource.class.getName());
resource.setProperty("driverClassName", "org.postgresql.Driver");
resource.setProperty("url", "jdbc:postgresql://localhost:5432/postgres");
resource.setProperty("username", "postgres");
resource.setProperty("password", "postgres");
context.getNamingResources()
.addResource(resource);
}
};
}
Because SpringBoot finds a custom Bean, there won't be an autoconfigured default one / it is overridden and the JNDI is successfully enabled.
However, as soon as I extract this Bean configuration into my auto-configure module of my custom SpringBoot Starter, the following exception is thrown while trying to start the sample SpringBoot application:
org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to multiple ServletWebServerFactory beans : tomcatServletWebServerFactory,tomcatFactory
I reckon this is due to SpringBoot not finding a customized Bean and therefore creating an autoconfigured default one which also won't be overridden. So now there will be two ServletWebServerFactory beans, the default one and the one from my auto- configure module.
What i tried so far (to no avail) :
annotating my custom Bean with #Primary
setting spring.main.allow-bean-definition-overriding to true
Is there any way to make SpringBoot not initialize a autoconfigured default bean, or any other possible solution to this?
try this
#AutoConfigureBefore(ServletWebServerFactoryAutoConfiguration.class)
I was able to solve this myself by excluding the responsible AutoConfiguration class:
#SpringBootApplication ( exclude = ServletWebServerFactoryAutoConfiguration.class)

Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean while loading the context xml in run method

#Configuration
#EnableAutoConfiguration
#ComponentScan
#SpringBootApplication
public class InitService extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run("classpath:abc-server.xml", args);
}
}
++++++++++++++++++++++++++++++++++++++++++
Here i am trying to migrate the Spring MVC project to Spring boot Standalone jar with embedded tomcat. So i tried loading the context xml(abc-server.xml) used in the existing project. When i run/deploy the spring boot jar, the following exception is thrown.
++++++++++++++++++++++++++++++++++++
[2015-05-19 15:12:30,012] ERROR org.springframework.boot.SpringApplication - Application startup failed
org.springframework.context.ApplicationContextException: Unable to start embedded container; nested exception is org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRefresh(EmbeddedWebApplicationContext.java:133)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:474)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:686)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:320)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:957)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:946)
at com.gogo.asp.server.init.InitService.main(InitService.java:188)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:53)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.getEmbeddedServletContainerFactory(EmbeddedWebApplicationContext.java:183)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.createEmbeddedServletContainer(EmbeddedWebApplicationContext.java:156)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRefresh(EmbeddedWebApplicationContext.java:130)
... 13 more
When you call run, you're only providing server-abc.xml as a source for your application's configuration:
SpringApplication.run("classpath:abc-server.xml", args);
That means that InitService is being ignored, including the fact that you've enabled auto-configuration. Without auto-configuration being switched on, Spring Boot will not automatically configure an embedded servlet container for you. You need to provide both InitService and abc-server.xml as configuration for your application.
I would provide InitService.class to SpringApplication.run and use #ImportResource to pull in your old XML configuration:
#SpringBootApplication
#ImportResource("classpath:abc-server.xml")
public class InitService {
public static void main(String[] args) {
SpringApplication.run(InitService.class, args);
}
}
Note that #SpringBootApplication is equivalent to #ComponentScan, #Configuration, and #EnableAutoConfiguration. You can just use #SpringBootApplication and drop the other three annotations as I've done above.

Spring Data Rest Boot app fails to start with Java config class

I'm trying to run simple Spring Data Rest Boot app (v1.2.3.RELEASE) with only one small modification from working Spring reference example app (http://spring.io/guides/gs/accessing-mongodb-data-rest/) and it failed to start.
To be more specific when I use:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
and just following simple code configuration:
public class Application {
public static void main(String[] args){
SpringApplication.run(Config.class, args);
}
}
#SpringBootApplication
public class Config {
}
without anything else I'm getting following error on startup:
2015-04-20 12:07:32.250 ERROR 5693 --- [ main]
o.s.boot.SpringApplication : Application startup failed
org.springframework.context.ApplicationContextException: Unable to
start embedded container; nested exception is
org.springframework.boot.context.embedded.EmbeddedServletContainerException:
Unable to start embedded Tomcat ...
Caused by:
java.lang.ClassCastException:
jug.ua.json.test.Config$$EnhancerBySpringCGLIB$$79797226 cannot be
cast to
org.springframework.data.rest.core.config.RepositoryRestConfiguration
at
org.springframework.boot.autoconfigure.data.rest.SpringBootRepositoryRestMvcConfiguration$$EnhancerBySpringCGLIB$$3a999d99.config()
...
However following code configuration is working fine:
#SpringBootApplication
public class Application {
public static void main(String[] args){
SpringApplication.run(Application.class, args);
}
}
Also if instead I use:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
separate Java config class approach is working fine as well...
What I'm doing wrong, cause I can't believe I spotted such an obvious bug?
Thank you,
Oleg
The problem appears to be due to a name clash between the config bean method on SpringBootRepositoryRestMvcConfiguration (inherited from Spring Data REST's RepositoryRestMvcConfiguration) and your configuration class named Config. Renaming it to something other than Config should get things working again.

spring-boot with tomcat and cxf-servlet

I'm trying to stand up an embedded Tomcat with spring-boot. I want to use CXF for a set of web services in the app but I can not figure out how to stand up the CXF servlet.
My Main class looks like this...
#Configuration
#EnableAutoConfiguration
#ComponentScan(basePackages={"com.connecture.services.documentservice.webservice"})
public class Application {
public static void main(String[] args) {
SpringApplication.run(new Class[] { Application.class, CfxInitializer.class }, args);
}
#Bean
public EmbeddedServletContainerFactory embeddedServletContainerFactory() {
TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory("", 8080);
return factory;
}
}
And my CfxInitializer like this...
public class CfxInitializer implements ServletContextInitializer
{
#Override
public void onStartup(ServletContext servletContext) throws ServletException
{
XmlWebApplicationContext rootContext = new XmlWebApplicationContext();
rootContext.setConfigLocations(new String[] { "classpath*:applicationContext.xml" });
servletContext.addListener(new ContextLoaderListener(rootContext));
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("CXFServlet", CXFServlet.class);
dispatcher.addMapping("/api/*");
}
}
When I try to build and start the jar with the typical command ./gradlew build && java -jar build/libs/gs-spring-boot-0.1.0.jar
I get an Exception for multiple Contexts.
Java.lang.IllegalStateException: Cannot initialize context because there is already a root application context present - check whether you have multiple ContextLoader* definitions in your web.xml!
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:277)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4971)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5467)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Here is a more complete pastebin - http://pastebin.com/bcJ2ULhM
----------------------------------------------------------------------------------
Similarly to Dave's answer I was able to fix it by removing the ServletContextInitializer and adding a bean to the Application Class.
#Bean
public ServletRegistrationBean servletRegistrationBean(){
return new ServletRegistrationBean(new CXFServlet(),"/api/*");
}
The Spring Boot embedded servlet features are designed to work with Servlet and ServletRegistration #Beans, and not with the ContextLoaderListener (which looks like it is trying to steal the ServletContext attribute for the root context). Try adding a ServletRegistration for your servlet instead; if it is Spring aware, assuming it has an interface that lets you change the application context or the context location, then you should be able to configure it in the registration.
I found the following project on github which helped me get started
https://github.com/ungerts/spring-boot-jaxrs-sample
Worked for me with:
spring-boot 1.2.3
cxf-rt-frontend-jaxrs:3.1.0
jackson-jaxrs-json-provider:2.5.3
Samples are now part of the CXF wiki: http://cxf.apache.org/docs/jaxrs-services-configuration.html#JAXRSServicesConfiguration-SpringBoot

Loading Resources with the Context Loader fails with a NullPointerException

I'm just wondering why I cannot load a resource with the Thread context loader in Felix OSGi? Am I not supposed to touch the context loader, am I doing something wrong or is it a bug?
I've a super simple bundle with a simple Activator:
public class Activator implements BundleActivator {
public void start(BundleContext context) throws Exception {
System.out.println("Hello World!!");
String resourcePath = "META-INF/mySuperDuperResource.txt";
// works
System.out.println(Activator.class.getClassLoader().getResource(resourcePath));
// null-pointer exception
System.out.println(Thread.currentThread().getContextClassLoader().getResource(resourcePath));
}
public void stop(BundleContext context) throws Exception {
System.out.println("Goodbye World!!");
}
}
Now loading the resource with with the class loader with the Activator.class.getClassLoader works. But not with the Thread.currentThread().getContextClassLoader(). There I get:
ERROR: Bundle info.gamlor.osgi [26] Unable to get module class path. (java.lang.NullPointerException)
java.lang.NullPointerException
at org.apache.felix.framework.BundleRevisionImpl.calculateContentPath(BundleRevisionImpl.java:410)
at org.apache.felix.framework.BundleRevisionImpl.initializeContentPath(BundleRevisionImpl.java:347)
at org.apache.felix.framework.BundleRevisionImpl.getContentPath(BundleRevisionImpl.java:333)
at org.apache.felix.framework.BundleRevisionImpl.getResourceLocal(BundleRevisionImpl.java:472)
at org.apache.felix.framework.BundleWiringImpl.findClassOrResourceByDelegation(BundleWiringImpl.java:1432)
at org.apache.felix.framework.BundleWiringImpl.getResourceByDelegation(BundleWiringImpl.java:1360)
at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.getResource(BundleWiringImpl.java:2256)
at info.gamlor.osgi.Activator.start(Activator.java:23)
at org.apache.felix.framework.util.SecureAction.startActivator(SecureAction.java:641)
at org.apache.felix.framework.Felix.activateBundle(Felix.java:1977)
at org.apache.felix.framework.Felix.startBundle(Felix.java:1895)
at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:944)
...
org.osgi.framework.BundleException: Activator start error in bundle info.gamlor.osgi [29].
at org.apache.felix.framework.Felix.activateBundle(Felix.java:2027)
at org.apache.felix.framework.Felix.startBundle(Felix.java:1895)
at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:944)
at org.apache.felix.gogo.command.Basic.start(Basic.java:729)
...
Caused by: java.lang.NullPointerException
at org.apache.felix.framework.BundleRevisionImpl.getResourceLocal(BundleRevisionImpl.java:474)
at org.apache.felix.framework.BundleWiringImpl.findClassOrResourceByDelegation(BundleWiringImpl.java:1432)
at org.apache.felix.framework.BundleWiringImpl.getResourceByDelegation(BundleWiringImpl.java:1360)
at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.getResource(BundleWiringImpl.java:2256)
at info.gamlor.osgi.Activator.start(Activator.java:23)
at org.apache.felix.framework.util.SecureAction.startActivator(SecureAction.java:641)
at org.apache.felix.framework.Felix.activateBundle(Felix.java:1977)
... 32 more
Now when just set the thread context class loader it works just fine:
Thread.currentThread().setContextClassLoader(Activator.class.getClassLoader());
But that has a hacky feeling to it. Feels like that will bite me later.
I'm not sure why your surprised this happens. A thread's context classloader is, by default, set to the classloader of it's parent, which in the beginning is set to the system classloader. So, assuming you don't do anything special, the context classloader is the system classloader, which is not the same as your bundle's classloader, hence it can't find your resource.
I agree that setting the context classloader has a hacky feel to it, but some libraries require this. I would do something like,
ClassLoader previous = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
badlyBehavedLibraryCall();
Thread.currentThread().setContextClassLoader(previous);

Resources