Issues Converting Mule Tomcat WAR to Spring Boot - java

I've been going through the process of converting my Mule project to a Spring Boot application, and have hit a snag I can't seem to figure out.
I'm pretty new to Spring Boot so I'm not sure if my issues lie with it, or with the way I'm doing my mule stuff.
Here is my sample project I've been trying to convert: https://github.com/JustinBell/mule-webapp-example
When I deploy this to a tomcat instance it works great, the issue comes when I try to run it as a Spring Boot application I'm getting this exception:
ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.
Just as a note I'm moving from mule 3.6.1 to 3.7.0-M1 as that's required (from my understanding) to use Spring Boot.
I've tried looking around for support on this issue which seems to pretty common, but none of the suggestions I've found have solved the issue.
Thanks for any help with these issues!

There are a few things that aren't quite right in your code as it stands.
If you want to build a web app with Spring Boot, you'll typically want to add a dependency on spring-boot-starter-web. This provides, among other things, the embedded servlet container:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
Your app's dependency on org.mule.transports:mule-transport-servlet pulls in a very old version of Tomcat's Coyote module. You need to exclude this to avoid it clashing with the up-to-date dependency that's provided by spring-boot-starter-web:
<dependency>
<groupId>org.mule.transports</groupId>
<artifactId>mule-transport-servlet</artifactId>
<version>${mule.version}</version>
<exclusions>
<exclusion>
<groupId>org.apache.tomcat</groupId>
<artifactId>coyote</artifactId>
</exclusion>
</exclusions>
</dependency>
Your Application class is trying to run MuleContextInitializer which it also declares as a bean. It should be running Application.class:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
// ...
}
Your Application class is also in the default package. You should avoid using the default package as it will cause Spring Boot to scan then entire classpath looking for your application's classes and configuration. Moving it into a package of its own to stop this from happening.
Lastly, the app fails to launch as it's looking for a file named mule-config.xml. Renaming mule-webapp-demo.xml to mule-config.xml addresses this.

I believe autodelete is an Enterprise feature, perhaps you are using ftp rather than ftp-ee.

Related

Change CodeStar Spring MVC project to Spring Boot

I have a Spring Boot project that works perfectly when run in IDE. I would like to run this via AWS CodeStar. Unfortunately, the default Spring template created by CodeStar uses Spring MVC.
I cannot just overwrite the default Spring MVC project with my Spring Boot project (it doesn't work). I can copy some of my resources to the MVC project, for example index.html and that works. But then features like Thymeleaf don't work. For this and other reasons, I would like to change the provided Spring MVC into the Spring Boot structure I already have.
I followed the instructions here: https://www.baeldung.com/spring-boot-migration
Unfortunately, this doesn't help. I can create Application Entry Point and add Spring Boot dependencies without the app breaking. But when I remove the default dependencies or the configuration associated with the MVC, the app breaks. When trying to reach the URL, I get a 404 error with description:
The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.
Debugging this error message (e.g. https://www.codejava.net/java-ee/servlet/solved-tomcat-error-http-status-404-not-found) didn't help.
The message seems like it's connected to the web resource. I have my web resources in folder resources as well as webapp/resources. And Spring Boot doesn't need any location configuration, right? It uses this location by default.
Can somebody tell me what things to remove and what to add to be able to use my existing Spring Boot project?
EDIT:
This is a link to a default template for AWS CodeStar Spring web application: https://github.com/JanHorcicka/AWS-codestar-template
And this is my Spring Boot project structure:
I realize that you indicated that previously you tried to use your Spring Boot project with some modifications without success, but I think it could be actually a possibility to successfully deploy your application on AWS CodeStar, and it will be my advice.
I also realized that in your screenshot you included several of the required artifacts and classes, but please, double check that you followed these steps when you deployed your application to AWS CodeStar.
Let's start with a pristine version of your Spring Boot project running locally, without any modification, and then, perform the following changes.
First, as indicated in the GitHub link you shared, be sure that you include the following files in your project. They are required for the deployment infrastructure of AWS:
appspec.yml
buildspec.yml
template.yml
template-configuration.json
The whole scripts directory
Please, adapt any necessary configuration to your specific needs, especially, template-configuration.json.
Then, perform the following modifications in your pom.xml. Some of them are required for Spring Boot to work as a traditional deployment and others are required by the deployment in AWS CodeStar.
Be sure that you indicate packaging as war:
<packaging>war</packaging>
To ensure that the embedded servlet container does not interfere with the Tomcat to which the war file is deployed, either mark the Tomcat dependency as being provided as suggested in the above-mentioned documentation:
<dependencies>
<!-- … -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<!-- … -->
</dependencies>
Or exclude the Tomcat dependency in your pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
If necessary, apply this exclusion using some kind of profile that allows you to boot Spring Boot locally and in an external servlet container at the same time.
Next, parameterize the maven war plugin to conform to the AWS CodeStar deployment needs:
<build>
<pluginManagement>
<plugins>
<!-- ... -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
<configuration>
<warSourceDirectory>src/main/webapp</warSourceDirectory>
<warName>ROOT</warName>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
<!-- ... -->
<plugins>
</pluginManagement>
</build>
I do not consider it necessary, but just to avoid any kind of problem, adjust the name of your final build:
<finalName>ROOT</finalName>
Lastly, as also indicated in the Spring documentation, be sure that your MyProjectApplication - I assume this class is your main entry point subclass SpringBootServletInitializer and override the configure accordingly, something like:
#SpringBootApplication
public class MyProjectApplication extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(MyProjectApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(MyProjectApplication.class, args);
}
}
Please, feel free to adapt the class to your specific use case.
With this setup, try to deploy your application and see if it works: perhaps you can find some kind of library dependencies problem, but I think for the most part it should work fine.
At a first step, you can try to deploy locally the version of the application you will later deploy to AWS CodeStar following the instructions you provided in your project template, basically, once configured with the necessary changes described in the answer, by running:
mvn clean package
And deploying the generated war on your local tomcat environment. Please, be aware that probably the ROOT application already exists in a standard tomcat installation (you can verify it by inspecting the webapps folder): you can override that war file.
For local testing you can even choose a different application name (configuring build.finalName and the warName in your pom.xml file): the important thing is verify if locally the application runs successfully.
If you prefer to, you can choose to deploy the app directly to AWS CodeStar and inspect the logs later it necessary.
In any case, please, pay attention on two things: on one hand, if you have any absolute path configured in your application, it can be the cause of the 404 issue you mention in the comments. Be aware that your application will be deployed in Tomcat with context root '/'.
On the other hand, review how you configured your database access.
Probably you used application.properties and it is fine, but please, be aware that when employing the application the database must be reachable: perhaps Spring is unable to create the necessary datasources, and the persistence manager or related stuff associated with and, as a consequence, the application is not starting. Again, it may be the reason of the 404 error code.
To simplify database connectivity, for testing, at first glance, I recommend you to use simple properties for configuring your datasource, namely the driver class, connection string, username and password. If that setup works properly, you can later enable JNDI or what deemed necessary.
Remember that if you need to change your context name and/or define a datasource pool in Tomcat you can place a context.xml file under a META-INF directory in your web app root path.
This context.xml should look like something similar to:
<?xml version="1.0" encoding="UTF-8"?>
<Context path="/">
<Resource name="jdbc/myDS"
type="javax.sql.DataSource"
maxActive="100"
maxIdle="30"
maxWait="10000"
url="jdbc:mysql://localhost:3306/myds"
driverClassName="com.mysql.jdbc.Driver"
username="root"
password="secret"
/>
</Context>

javax.servlet.ServletContext is being loaded from multiple dependencies in springboot application

So I have this springboot application which I'm migrating from a WAS to a springboot setup. And I have a couple of JSPs which has to be configured. To accomodate these I added the following dependencies:
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>9.0.22</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<scope>provided</scope>
</dependency>
The application already came with the following dependency which is being used throughout the application:
<dependency>
<groupId>com.ibm</groupId>
<artifactId>com.ibm-jaxrpc-client</artifactId>
<version>6.0</version>
</dependency>
The issue I'm facing is that both these dependencies (jaxrpc-client and tomcat-embed-jasper) have javax.servlet.ServletContext classes in them which is causing the following error:
The method's class, javax.servlet.ServletContext, is available from the following locations:
jar:file:/C:/Users/.m2/repository/com/ibm/com.ibm-jaxrpc-client/6.0/com.ibm-jaxrpc-client-6.0.jar!/javax/servlet/ServletContext.class
jar:file:/C:/Users/.m2/repository/org/apache/tomcat/embed/tomcat-embed-core/9.0.30/tomcat-embed-core-9.0.30.jar!/javax/servlet/ServletContext.class
It was loaded from the following location:
file:/C:/Users/.m2/repository/com/ibm/com.ibm-jaxrpc-client/6.0/com.ibm-jaxrpc-client-6.0.jar
Action:
Correct the classpath of your application so that it contains a single, compatible version of javax.servlet.ServletContext
I can't afford to remove any of these dependencies. jaxrpc-client is being referenced in the code already in too many places and I need tomcat-embed-jasper to render my jsp pages. I can't exclude the ServletContext class since its not a dependency(If I'm not wrong about the concept of exclusion). Please help with with a way forward with this issue.
I'm not familiar with IBM's jaxrpc client, but I assume, you have this exception in runtime, when trying to load the application.
In this case consider the following approaches:
Use another jax-rpc client library
Consider Loading the code that uses this library with the different class-loader (you'll have to create one classloader for this) to avoid the clash
Kind of paraphrasing the second option. You can "play" (override the order of loading of specific classes) with spring boot classloader as described in this article
I know, this is too general answer, but hopefully its still helpful.
The first solution is by far the easiest way I can think of.
The second solution is doable, however it pretty much depends on how exactly the code that uses the jax rpc client is loaded and used.

Spring Boot application + Jolokia - exception during startup

I'm using Spring Boot 1.5.3.RELEASE and Jolokia 1.3.6 (also happens in later versions).
The Jolokia is integrated by adding a dependency:
<dependency>
<groupId>org.jolokia</groupId>
<artifactId>jolokia-core</artifactId>
</dependency>
One of our microservices that all share the same architecture fails to start and I see the exception with the following root-cause during the startup:
Caused by: java.io.FileNotFoundException: JAR entry BOOT-INF/lib/jolokia-core-1.3.7.jar!/META-INF/simplifiers-default not found in <MY_JAR_NAME_GOES_HERE>.jar
at sun.net.www.protocol.jar.JarURLConnection.connect(JarURLConnection.java:142)
at sun.net.www.protocol.jar.JarURLConnection.getInputStream (JarURLConnection.java:150)
at java.net.URL.openStream(URL.java:1045)
at org.jolokia.util.ServiceObjectFactory.readServiceDefinitionFromUrl(ServiceObjectFactory.java:90)
This exception doesn't happen when I start the application from the IDE, only when I start with java -jar <MY_JAR>.
I looked at the line that produces exception inside the code of Jolokia, and it looks like this:
reader = new LineNumberReader(new InputStreamReader(new URL(pUrl).openStream(),"UTF8"));
So I conclude (after debugging) that new URL(pUrl).openStream() fails to find a jar entry as specified in the aforementioned exception stack trace. I also understand that in IDE it doesn't happen because it works with different classloaders (Spring Boot application uses LaunchedURLClassLoader).
However, I don't see a bug here in the source code: we have a lot of microservices, all are running with the same configurations and it works as expected, in addition, as far as I know this is the documented way for Jolokia integration.
So I suspect some race condition here or something, but I can't really point out exactly what happens here.
Did anyone encounter such a problem? Is there a workaround?
I was getting exactly the same exception. The problem in my case was that the filename had a + (I'm using reckon Gradle plugin to generate the project version). The solution was to rename the file before running it with java -jar.
I'm facing the same problem, with Spring Boot 1.5.22 and default version of jolokia.
I have another app (same version of SpringBoot, jolokia) that did not have the problem... I did not find any differences between the 2 apps...
But I have use that workaround : instruct Spring Boot to extract jolokia jar in order to skip Spring boot nested jar url process for jolokia jar only.
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<requiresUnpack>
<dependency>
<artifactId>jolokia-core</artifactId>
<groupId>org.jolokia</groupId>
</dependency>
</requiresUnpack>
</configuration>
</plugin>
see https://docs.spring.io/spring-boot/docs/1.5.x/reference/htmlsingle/#howto-extract-specific-libraries-when-an-executable-jar-runs
With this workaround, jolokia is happy, the /jolokia endpoint is available and Spring Boot Admin jmx tab is active.

Spymemcached conflict with hibernate-memcached and webapp-runner

I've got a Heroku Java app that makes use of the Spymemcached library, which in my case is included by my use of the hibernate-memcached library (1.3).
I now need to make sure that all requests to my app go over HTTPS. This led me to this post, where the solution pivots on making use of the webapp-runner plugin and some config to get the right headers to my app (you provide the runner a context.xml).
My problem is that the webapp-runner plugin has a dependency (further down the dependency graph) on the Spymemcached library as well, which causes a conflict on start up. Furthermore, I can't downgrade webapp-runner to 7.0.22.1 as suggested by this post, as the support for specifying the context.xml came after the fact.
So I thought it would be a simple matter of excluding Spymemcached from my hibernate-memcached dependency so that only the webapp-runner's Spymemcached source would be included:
<dependency>
<groupId>com.googlecode</groupId>
<artifactId>hibernate-memcached</artifactId>
<version>1.3</version>
<exclusions>
<exclusion>
<artifactId>hibernate</artifactId>
<groupId>org.hibernate</groupId>
</exclusion>
<exclusion>
<groupId>spy</groupId>
<artifactId>spymemcached</artifactId>
</exclusion>
</exclusions>
</dependency>
But for some reason I still get the conflict on start up - on the factory bean that creates my memcachedClient which I specify in my application context:
<bean id="memcachedClient" class="net.spy.memcached.spring.MemcachedClientFactoryBean">...</bean>
Resulting in the infamous java.lang.NoClassDefFoundError:
Error loading class [net.spy.memcached.spring.MemcachedClientFactoryBean] for bean with name 'memcachedClient' defined in file [/home/markus/coding/reader/target/tomcat.8080/work/Tomcat/localhost/_/WEB-INF/classes/META-INF/spring/applicationContext.xml]: problem with class file or dependent class; nested exception is java.lang.NoClassDefFoundError: org/springframework/beans/factory/FactoryBean
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:328)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:106)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveManagedList(BeanDefinitionValueResolver.java:353)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:153)...
When I search for the MemcachedClientFactoryBean in my IDE I can see that it's made available by the webapp-runner and not hibernate-memcached, so the exclusion seemed to have done something right.
Am I missing something obvious here? How do I get rid of this NoClassDefFoundError?
FYI I found out that version 7.0.22 of webapp-runner does indeed have support for providing it a context.xml by running java -jar target/webapp-runner.jar --help
It differs slightly to the later versions where you specify ... --context_xml ... instead of ... --context-xml ...
Version 7.0.22 of webapp-runner doesn't have Spymemcached as a dependency, which solves the problem.

WebSphere MQ Configuration issues

getting below error after i configure MQ connection factory.
java.lang.ClassCastException: com.ibm.ejs.jms.JMSQueueConnectionFactoryHandle incompatible with com.ibm.mq.jms.MQQueueConnectionFactory
my code snippet where the exception is pointing to :
String queueConnectionJndi = props.getProperty(queueConnection + MQ_CONN);
queueConnectionFactory = MQQueueConnectionFactory)initialContext.lookup(queueConnectionJndi);
I am not able to find out the root cause of this.
can any body please help me on this, Thanks in advance.
There is no way to be sure without more context, but it looks like this method call:
initialContext.lookup(queueConnectionJndi);
is returning an object of type com.ibm.ejs.jms.JMSQueueConnectionFactoryHandle which cannot be cast to com.ibm.mq.jms.MQQueueConnectionFactory.
Can you provide more context?
This Post on old nabble sounds like a similar issue and may help you out.
Specifically the final response talks about removing any jms.jar file(s) that may be in your deployed WAR. Check your WEB-INF/lib. Certain jars are provided by the Websphre container and shouldn't be including them in your WAR.
This Post on the spring fourm also indicates issues of this nature caused by jars included in the classpath that shouldn't be there
Remove any of the following if you find them...
naming.jar
providerutil.jar
jndi.jar
jms.jar
mq.jar
websphere.jar
Can you rewrite your code to use JMS standard (ConnectionFactory or QueueConnectionFactory)instead of a Websphere MQ specific implementation class? That way you won't be tying your app to Websphere MQ and porting it to an alternative MQ implementation would be easier...
i.e.
import javax.jms.QueueConnectionFactory;
...
queueConnectionFactory = (QueueConnectionFactory)initialContext.lookup(jndiName);
the MQ jars which WAS is using and my application using are different so this problem occured. when i corrected the classpath it is resolved. sorry for the trouble, thanks for the help.
I went through a lot of trial and error to find the answer (the answer to my question at least). I hope this solution will solve your issues too. As mentioned from another post excluding the jms library works. But how do you exclude the jms library and still be able to compile the code? That was something no one seems to have mentioned. The solution to that is to make the scope for the jms library to "provided" (if you are using Maven or Gradle).
As mentioned somewhere:
"Provided means that you need the JAR for compiling, but at run time there is already a JAR provided by the environment so you don't need it packaged with your app. For a web app, this means that the JAR file will not be placed into the WEB-INF/lib directory."
So in your pom.xml add/update these:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>4.3.4.RELEASE</version>
<exclusions>
<exclusion>
<groupId>javax.jms</groupId>
<artifactId>javax.jms-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
<version>1.1</version>
<scope>provided</scope>
</dependency>
Hopefully this can be helpful to those who have been frustrated by the lack of answers from the Internet.
Remove all the ibm libraries. They are useless. Once you deploy onto Websphere, it will use its libraries anyways.

Categories