spring boot without tomcat in a standalone jar - java

I have a project structure with three jar files. All are loaded into one classpath and then get executed. I am using the spring core, jpa and hibernate. #Services/#Autowired are working fine, as well as Entities and Repositories (all on a mysql database).
Now I want that the project can send and receive messages over network/internet. So I asked some people how I could achieve this without breaking my current structure. And I was told that spring-boot is the architecture for me because I do not need a web server (tomcat or glassfish) for it.
But now I am not sure if this is correct because I did not find any sources that say the same thing. Because of that I tried implementing it in order to verify it myself.
The important changes I made to my project (all pom.xml files and my configuration class) can be found here: http://84.141.90.123:9910/
From what I read I need the #SpringBootApplication annotation for spring boot. This is a equivalent to #Configuration, #ComponentScan, #EnableAutoConfiguration, #EnableWebMvc.
The first two are already in my structure. But when I add the last two annotations, I get different errors:
When I add #EnableAutoConfiguration I get
Caused by: java.lang.IllegalArgumentException: No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.
When I add #EnableWebMvc I get
Caused by: java.lang.IllegalArgumentException: A ServletContext is required to configure default servlet handling
From my bad english knollage, the #EnableWebMvc error seems to say that the application needs a web server (tomcat or glassfish).
So is the main statement wrong and I can not start spring boot without a web server?
Because I do not use any xml files and/or property files for spring (they did not worked), I only rely on java code based configuration for spring, jpa and hibernate. And therefore there are only very few tutorials/threads with help. Most of the time they just say add thi or add that to your xml but because I don't have them, it is a little pain in the ass.
Also I compile with aspectj, so I can not use the spring compile parent. And also I am not able to manipulate the main class/method, because the main class is in an outer jar file that is not programmed by me.
So concrete:
Can a spring boot application in a standalone jar run without a web server wrapping it?
If yes, what am I doing wrong? Am I missing a dependency, an annotation or a configuration?

If you want to receive messages over HTTP(ie run a REST API) then you need a web server.
If you just want to send messages over HTTP then you only need a HTTP client.
Spring-boot has the the option of running an embedded web server(tomcat by default), you don't need to run a separate application server.
To work out your issues with your build I would start with generating a project using spring initializr.
You can select the dependencies you want(try using the advanced version link at the bottom), and it will build a maven/gradle project for you with the right structure, build file and compatible dependencies.

Change :
public static void main(String[] args) {
SpringApplication.run(ActiveMqApplication.class, args);
}
to :
public static void main(String[] args) {
new SpringApplicationBuilder(ActiveMqApplication.class)
.web(WebApplicationType.NONE).run(args);
}

Related

Is it possible to resolve REST end points of a dependency JAR file in spring Boot

I have a 2 Spring boot jars which work fine as 2 independent applications, however, I have been asked to merge 2 jars into a single application
The easiest thing I thought would be to add app-2 as a maven dependency into app-1 but the problem is that when the app-1 starts it only recognises the app-1 REST endpoints but ignores REST endpoint of app-2 altogether.
I was hoping that when the app-1 starts it will automatically pick the endpoints declared in app-2
#RestController
Class2{
#GetMapping(/hello-from-app2)
public String myapp2(){
return "HELLO FROM APP2"
}
This code gets ignored and at server start up I can only see the endpoints exposed by app-1 are visible.
If you are including App2.jar as a dependency into App1.jar, the best approach would be to import the Configuration of App2. If you start adding scans and stuff you would be tightly coupling you App1 to your App2. App1 would have to know implementation details of App2 that doesn't need to.
If you just import the configuration of App2, the configuration details would remain encapsulated.
I assume you have a Java Config class (or an XML Config file) for App1 and another one for App2. I also assume that the config of App2 contains all the necessary annotations for component scanning and the correct base-packages.
If that's the case, you can add an import like this and it should work right away:
#Configuration
#Import(SpringConfigurationApp2.class)
public class SpringConfigurationApp1 {
//... some beans....
}
I saw answer provided by #Diego but with that user need to make changes in client application. (ex. #Import(SpringConfigurationApp2.class) here).
I have another approach where client (App-1) does not need to make any change in application. It will just work seamlessly. This approach is by use of spring's auto configuration and same feature is used by spring-boot dependency.
Here is my answer to achieve using autoconfiguration.
For App-2,
1) create spring.factories file under resources/META-INF
2) Add org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
path-to-app-2-application/SpringConfigurationApp2
For App-1,
Just include App-2 as maven dependency and you are Done.
Here is a link to get more information about https://dzone.com/articles/what-is-spring-boot-auto-configuration.

Is it possible to nest a Spring Boot application inside another Spring Boot application?

I have a Spring Boot RESTful microservice that a customer would like to nest inside their Spring Boot application.
Could someone tell me whether this is possible?
I was hoping this would be as simple as adding a dependency on my application in the customers maven pom file and then excluding the tomcat dependency since the customer already uses the embedded tomcat.
Thanks,
Ben
Since they already use Spring Boot to start their app, you can simply mark all Spring Boot dependencies as provided in your Maven POM, this would exclude it from the JAR as well as embedded Tomcat and all related dependencies. Also make sure you don't build your JAR as a Spring Boot executable (should be the default if you're not using the spring-boot-maven-plugin).
On the customer side, they would need to include your JAR as a dependency, and possibly add a scanBasePackages property to their #SpringBootApplication, to auto-discover your application classes, if they don't reside in a package under the one that #springBootApplication is on. Also, they'll need to be mindful of any URI collisions between your app and theirs, as the two will be sharing the same environment.

spring boot parent for jar file

I want to create a jar file that I can add to a classpath and will basically "plug-in" to an existing spring boot application. I need it to be able to have annotations of Component, ConfigurationProperties, and all the fun things that spring boot gives you, but I want it "thin" and it will be a jar file used as part of a full spring boot web application.
I need the jar file to be externally configurable. Property files will be different for different deployments. So having a working #Configuration annotation is critical.
I look at spring-boot-starter-parent, and that has jetty, tomcat, hibernate stuff and is a huge jar file. I don't want that.
Is there a "thin" parent?
Is spring boot simply not what I want here? And I should just use a regular spring project and set my "Main" spring boot web app to do component scans to configure the jar file?
It sounds like you are trying to define your own Spring Boot Starter. That's the real power that Spring Boot gives you, the ability to include a dependency and have it auto-configure itself.
By packaging your jar the right way, Spring Boot will detect that there are configurations, components, and property files automatically. I've used this in the past for the case where I want all of my applications to log a specific way, or enforce a certain format for my REST endpoints.
The documentation gives a very thorough overview of the steps you'll need to take. But essentially, you are going to package your jar like any other (with your #Bean, #Component, #Service, and #Configuration classes in it), and provide a property file pointing to the configurations:
// Example spring.factories file
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.mycorp.libx.autoconfigure.LibXAutoConfiguration,\
com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration
Also check out the set of #ConditionalOn... annotations, they can really help with controlling what beans become active based on properties being defined, profiles being active, or dependencies being loaded.

exception while trying to deploy liferay portlet with spring

I'm trying to create a portlet with liferay 6.2 and using spring. If I create a bean without using constructor-arg or factory-method then everything works fine. But if I use either of these then I get exceptions when the portlet is deployed.
an example:
the exception I'm getting is:
01:28:21,884 ERROR [ContextLoader:323] Context initialization failed
java.lang.IncompatibleClassChangeError: class org.springframework.core.LocalVariableTableParameterNameDiscoverer$ParameterNameDiscoveringVisitor has interface org.springframework.asm.ClassVisitor as super class
I realize that this can be caused by having 2 versions of ams, but im using the spring jars that come with liferay.
You give an option yourself - duplicate classes. But without knowing how you build and what you're doing, there's hardly anything to do apart from asking you to make extra extra extra sure that you don't have duplicate resources on the classpath:
Check your deployed web application (once it's deployed to your application server) and its WEB-INF/lib folder for such duplicates. They might come in only during the buildprocess, e.g. they might not be in your IDE's workspace. Or Liferay might inject them (due to declared dependencies) during deployment.
You'll have to figure out how (and in which phase) those resources get there, then eliminate that option (e.g. through proper maven scope, e.g. "provided")

Ease of rolling back from Spring Boot to regular Spring and viewing hybrid of Spring Context and configurations while using Spring Boot

I am assessing whether spring-boot and how I could migrate to using it.
One question I have is whether a project that uses spring boot can be converted easily back to a regular spring project which uses the traditional spring configuration files if that is required. This would be useful in my mind for a few reasons.
1) merging with legacy projects, because as I have read moving from legacy spring to spring-boot is somewhat tedious.
2) Obtaining a view of the spring application context file and webapp configuration files to understand what the actual configurations being used are.
Another question I have is regarding the lack of application-context file, is there a way to have some kind of hybrid where there is still an application-context file that can be seen? Part of my concern is that spring-boot auto configures components without us knowing and learning how they are configured and work together.
Spring Boot provides auto-configuration.
When #SpringBootApplication is encountered, it triggers a search of the CLASSPATH for a file called META-INF/spring.factories which is just a regular text file that enumerates a list of Java configuration classes. Java configuration was introduced in 2006 and then merged into Spring 3 in 2009. So it's been around for a long time. These Java configuration classes define beans in the same way that XML does. Each class is annotated with #Configuration and therein you find beans defined using methods (factory methods, or provider methods) whose return value is managed and exposed via Spring. Each such provider method is annotated with #Bean. This tells Spring to consider the method and its return value the definition of the bean.
Spring Boot tries to launch all the Java configurations it sees enumerated in that text file. It tries to launch RabbitAutoConfiguration.class, which in turn provides beans for connecting to RabbitMQ and so on. Of course, you don't want those beans in certain cases, so Spring Boot takes advantage of Spring framework 4's #Conditional mechanism to conditionally register those beans only if certain conditions are met: is a type on the CLASSPATH, is a property exposed through the environment, has there been another bean of the same type defined by the user, etc. Spring boot uses these to only create the RabbitMQ-specific beans if, for example, the dependencies that you would get from org.springframework.boot:spring-boot-starter-amqp are on the CLASSPATH. It also considers that the user may have provided a different implementation of RabbitTemplate in some othe rbean definition (be it XML or Java configuration) so it uses that if it's there.
These java configuration classes are the same sort of Java configuration classes you would write without Spring Boot. BUT... WHY? 80% of the time, the auto-configuration that Spring Boot provides is going to be as good or better than the configuration you would write yourself. There are only so many ways to configure Spring MVC, Spring Data, Spring Batch, etc., and the wager you take using Spring Boot is that the leaders and engineers on those various projects can provide the most sensible 80%-case configuration that you probably don't care to write, anyway.
So, yes you could use Spring Boot with existing applications, but you'd have to move those existing applications to Spring 4 (which is easy to do if you're using the spring-boot-starter-* dependencies) to take advantage of #Conditional. Spring Boot prefers: NO configuration, Java configuration, XML configuration, in that order.
If you have an existing application, I'd do the following:
find out what dependencies you can remove from your Gradle/Maven build and just have taken care of for you with the various spring-boot-starter- dependencies.
add #SpringBootApplication to a root component class. Eg, if your package is a.b.c, put a class Application in a.Application and annotate that with #SpringBootApplication
You can run it as a Servlet 3 application or in an embedded servlet container. It might be easier to just run in a standard servlet container as you take baby steps. Go to http://start.spring.io and make sure to choose war in the packaging drop down. Copy the ServletInitializer class and the specification from the pom.xml to ensure that your application is a .war, not a .jar. Then, once everything works on Spring Boot, rm -rf the Initializer and then revert the Maven build to a simpler .jar using the Spring Boot plugin for extra win.
If your application has lots of existing configuration, import it using #Import(OldConfig.class) or #ImportResource("old-config.xml") on the a.Application configuration class. The auto-configuration will kick in but it will see, for example, that you may have already defined a DataSource so it'll plug that in in the right places. What I like to do now is just start the application up, see if everything's OK, then start removing code in my old Java or XML configuration. Don't remove your business code, but remove things related to turning on parts of Spring. Anything that looks like #Enable..* or ..:annotation-driven/>. Restart and verify things still work. The goal is to let Spring Boot do as much of the heavy lifting as possible. Viewing the source is very handy here so you can see what Spring Boot will try to do for you. Spring Boot will log information on what #Conditional conditions evaluated to true for you. Simply provide --Ddebug=true wen you start the application to see the output. You could also export DEBUG=true then restart your IDE to run it as long as the environment variable is ivsible in your shell.

Categories