GenericConversionService, IdToEntityConverter and StackOverflowError

Spring GenericConversionService, IdToEntityConverter and StackOverflowError

Advertisements

Yesterday, after a code refactoring, doing a test of the web application I get a mythical StackOverflowError:

java.lang.StackOverflowError
at java.util.HashMap.addEntry(HashMap.java:888)
at java.util.LinkedHashMap.addEntry(LinkedHashMap.java:427)
at java.util.HashMap.put(HashMap.java:509)
at java.util.HashSet.add(HashSet.java:217)
at org.springframework.core.convert.support.GenericConversionService.addInterfaceHierarchy(GenericConversionService.java:452)
at org.springframework.core.convert.support.GenericConversionService.getMatchingConverterForTarget(GenericConversionService.java:437)
at org.springframework.core.convert.support.GenericConversionService.findConverterForClassPair(GenericConversionService.java:349)
at org.springframework.core.convert.support.GenericConversionService.getConverter(GenericConversionService.java:244)
at org.springframework.core.convert.support.GenericConversionService.canConvert(GenericConversionService.java:145)
at org.springframework.core.convert.support.IdToEntityConverter.matches(IdToEntityConverter.java:54)
...

strange … on a web form that had not been subject to code changes for a long time.

The project is based on Spring 3.1.0 and Java 7.

By debugging, I find that the failed conversion affects the domain class Address to which the static findAddress method has been added. The signature is as follows:

public static Address findAddress(Address address)

the method has been added to prevent duplicate persistance addresses on the db.

So now the Address class has two static finders:

public static Address findAddress(Address address)
public static Address findAddress(Long id)

the IdToEntityConverter class uses the getFinder method whose rules are (from javadoc):

the finder method must be public, static, have the signature find[EntityName]([IdType]), and return an instance of the desired entity type.

The problem is that IdType is converted by searching its finder, which is always of the same type Address, causes the loop resulting in StackOverflowError.

The solution is to rename

findAddress(Address address)

method in

retrieveAddress(Adress address).

Howto install web application with Spring Roo 2.0 on Websphere 8.5.5

Install web application with Spring Roo 2.0.0.RC1 on Websphere 8.5.5.12

To install spring roo 2.0.0.RC1 on Websphere 8.5.5.12 you have to solve some problems. The environment is as follows:

  • Ubuntu 15.04 (GNU/Linux 3.19.0-15-generic x86_64)
  • IBM J9 VM (build 2.8, JRE 1.8.0 Linux amd64-64 Compressed References 20170419_34
  • Spring Roo 2.0.0.RC1
  • Websphere 8.5.5.12

JPA problem

The first problem is the JPA compatibility. Roo 2.0.0.RC1 uses jpa 2.1 while WAS 8.5.5.12 jpa 2.0.At the start of the application there is such a mistake:

caused by: java.lang.ClassCastException: com.ibm.websphere.persistence.PersistenceProviderImpl incompatible with javax.persistence.spi.PersistenceProvider at javax.persistence.Persistence$1.isLoaded(Persistence.java:110) ~[hibernate-jpa-2.1-api-1.0.0.Final.jar:1.0.0.Final]

To fix this problem, you need to add the HibernatePersistenceProviderResolver class to your project:

HibernatePersistenceProviderResolver.java

and register it in the Application class in the onStartup method

@Override public void onStartup(ServletContext servletContext) throws ServletException {
HibernatePersistenceProviderResolver.register();
super.onStartup(servletContext); }

XML-APIS problem

The second problem is due to xml-apis jar:

Caused by: java.lang.ClassCastException: org.apache.xalan.processor.TransformerFactoryImpl incompatible with javax.xml.transform.TransformerFactory at javax.xml.transform.TransformerFactory.newInstance(Unknown Source) ~[xml-apis-1.4.01.jar:na]

to solve it you need to make an exclusion in pom.xml like this:

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
      <exclusions>
      	<exclusion> 
          <groupId>xml-apis</groupId>
          <artifactId>xml-apis</artifactId>
        </exclusion>        
      </exclusions>
    </dependency>

WEBJARS problem

Third issue concerns the webjars protocol:

Caused by: java.lang.NullPointerException at java.io.FilterInputStream.close(FilterInputStream.java:192) at sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream.close(JarURLConnection.java:121) at org.springframework.web.context.support.ServletContextResource.isReadable(ServletContextResource.java:120)

For some reason WAS uses the wsjar protocol instead of jar. Also to access the resources within the jars WAS must have a trailing slash in the path.To solve this, you need to add the following class:

WasWebJarAssetLocator.java

and log it in to WebMvcConfiguration in the addResourceHandlers method

@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { super.addResourceHandlers(registry); registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"). resourceChain(false).addResolver(new WebJarsResourceResolver(new WasWebJarAssetLocator())); }

finally adding variable to was (web container custom properties)

com.ibm.ws.webcontainer.SkipMetaInfResourcesProcessing = true

Transactions problem

Another issue with regard to transactions is:

Caused by: java.lang.LinkageError: com/ibm/websphere/uow/UOWSynchronizationRegistry.registerInterposedSynchronization(Ljavax/transaction/Synchronization;)V (loaded from file:/opt/IBM/WebSphere/AppServer/plugins/com.ibm.ws.runtime.jar by org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader@d30c8d80) called from class org.springframework.transaction.jta.WebSphereUowTransactionManager$UOWActionAdapter (loaded from file:/opt/IBM/WebSphere/AppServer/profiles/AppSrv01/installedApps/UBDSK01Node01Cell/uman-1_0_1_war.ear/uman-1.0.1.war/WEB-INF/lib/spring-tx-4.3.3.RELEASE.jar by com.ibm.ws.classloader.CompoundClassLoader@19d0788f[war:uman-1_0_1_war/uman-1.0.1.war]   Local ClassPath: com.ibm.ws.uow.embeddable.EmbeddableUOWManagerImpl.runUnderNewUOW(EmbeddableUOWManagerImpl.java:791) ~[com.ibm.ws.runtime.jar:na] ... 112 common frames omitted

to solve it you have to make some exclusions in the pom. For javax.transaction we make the exclusion and scope provided:

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
      <exclusions>
      	<exclusion> 
          <groupId>xml-apis</groupId>
          <artifactId>xml-apis</artifactId>
        </exclusion>
        <exclusion> 
          <groupId>javax.transaction</groupId>
          <artifactId>javax.transaction-api</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
<dependency>
      <groupId>javax.transaction</groupId>
      <artifactId>javax.transaction-api</artifactId>
      <scope>provided</scope>
    </dependency>

for jta we make the exclusion:

<dependency>
      <groupId>io.springlets</groupId>
      <artifactId>springlets-boot-starter-web</artifactId>
      <exclusions>
      	<exclusion>
      		<groupId>javax.transaction</groupId>
    		<artifactId>jta</artifactId>
      	</exclusion>
      </exclusions>
    </dependency>

that’s all.

Having done all this, the application will run smoothly.