How does Java runtime find my main class? - java

I am learning Spring Boot. I made a simple Spring Boot project that can output a hello world string at http://localhost:8080/welcome
I use Maven to build my project that would output a jar file.
To start up my spring boot app, I use the command as below
java -jar my-springboot-app.jar
My question is:
How is java smart enough to locate my main class and its main method (e.g. the application launcher)?
I checked the jar file and browsed those BOOT-INF & META-INF and could not find any clues.
Does the spring boot framework (#SpringBootApplication) or maven automatically do the magic for me?

In case of spring boot jar the things are little bit more complicated than regular jar. Mainly because spring boot applicaton jar is not really a JAR (by jar I mean something that has manifest and compiled classes). Regular JARs can be "recognized" and processed by jvm, however in Spring Boot there are also packed dependencies (take a look at BOOT-INF/lib) so its jars inside JARs. How to read this?
It turns out that spring boot always has its own main class that is indeed referred to in MANIFEST.MF and this a real entry point of the packaged application.
The manifest file contains the following lines:
Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: com.example.demo.DemoApplication
Main-Class is a JVM entry point. This class, written by spring developers, basically does two things:
- Establishes a special class loader to deal with a "non-regular-jar" nature of spring boot application. Due to this special class loaders spring boot application that contains "jars" in BOOT-INF/lib can be processed, for example, regular java class loaders apparently cannot do this.
- Calls the main method of Start-Class value. The Start-Class is a something unique to spring boot applications and it denotes the class that contains a "main" method - the class you write and the class you think is an entry point :) But from the point of view of the spring boot infrastructure its just a class that has an "ordinary" main method - a method that can be called by reflection.
Now regarding the question "who builds the manifest":
This MANIFEST.MF is usually created automatically by plugins offered by Spring Developers for build systems like Maven or Gradle.
For example, the plugin looks like this:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
During its work, this plugin identifies your main class (com.example.demo.DemoApplication in my example). This class is marked with #SpringBootApplication annotation and has a public static void main method.
However, if you put many classes like this the plugin probably won't recognize the correct class so you'll need to configure the plugin properties in POM.xml to specify the right class.

Java classes are executed within a larger context,
you run java -jar somejar.jar the class in question will be selected in the .jar file's manifest.
#SpringBootApplication will have componentscan, enabling auto configuration(autowired)
componentscan - to identify all the controller, service and configuration classes within the package.

Related

How to run a java task automatically from an external jar?

I want to generate a java jar which when included on the classpath of another project will launch a periodic task that does something in the background.
This is very similar to eureka client. You include the dependency and add an annotation after which a service is started automatically to poll eureka server.
How can I do that?
Edit: I got it to work using maven, following the example provided in the comments
github.com/shauank/spring-boot/tree/master/client (client which is having taskexecutor)
github.com/shauank/spring-boot/tree/master/application (Application which uses jar created in step1)
You can use concept of Autoconfiguration. Same is used by Eureka and Config server.
Under src/main/resource create spring.factories and add following entry
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
location.to.your.executor
Your class,
pacakage location.to.your.executor
class MyExecutor{
public MyExecutor(){
//Your code for task executor
}
}
Now, above code can be build as jar and included into another spring boot project.
So, when you run another jar, spring boot will look for auto configuration onto spring.factories class and load classes defined into it.

Spring Boot: class in package not being loaded

I'm having trouble with a Spring Boot app that is not loading a config class with 2 beans in it. The weird thing is that another config class in the same package gets loaded.
Both config classes have #Configuration in them. The one that doesn't load also has a #ComponentScan(basePackages = {"com.example.package.in.jar"}) in it.
The base packages value refers to a package in a loaded jar file.
I'm using Gradle 3.4.1, Spring Boot 1.5.3. When I turn on Spring debugging, it shows the other config class being found and loaded, but it just skips over the other one. No exceptions are thrown - no errors at all.
It would be one thing if the code didn't run, but at least load the class or throw an error, but the log file that was created showed no errors.
Use #EntityScan("com.yourpackage*") will check all the packages starts with "com.yourpackage"
Refer https://github.com/Roshanmutha/JPARepo_44149690/blob/master/src/main/java/com/rcmutha/usl/controller/Application.java
So it turns out that the problem was that it the file wasn't even being seen, period. After attempting the suggestions, I found another option to try: #ImportAutoConfiguration. I used this annotation in the main Spring Boot app file and specified the files in my config package. This is when the compiler said that it couldn't resolve the file I was having problems with.
I cut out the contents, deleted the file and re-created it with a slightly different name, pasted back the contents and update the file list for the annotation. It worked!
The file showed up in the file tree in IntelliJ, but it wasn't being seen by the compiler, so it wasn't being configured. Within the annotation it had to look for it explicitly, and then the error was produced.
Thank you to those that posted suggestions.
Les

Spring boot configuration in a multi-Module maven project

I'm having a problem properly setting up spring boot for my multi-module maven project.
There is a module "api" that uses another module "core". Api has an application.properties file that contains spring.mail.host=xxx. According to the spring boot documentation this provides you with a default implementation of the JavaMailSender interface, ready to be autowired.
However the class that is responsible for sending out the e-mails resides in the "core" package. When I try to build that module the build fails because no implementation of JavaMailSender can be found.
My guess then was that the mailing config should reside in "core" in a separate application.properties. I created that and moved the spring.mail.host property from the "api" to the "core" property file.
This time the core module builds successfully, but "api" fails to build because of the same exception, so I think I just moved the problem.
I don't understand the required structure for handling this type of situations well enough so I was wondering what the correct way is for having a "core" module containing all the correct configuration for sending mails and having other modules use the mailing code and config that resides in it.
I found the answer in another stack overflow question: How to add multiple application.properties files in spring-boot?
It turns out there can only be 1 application.properties file in the final jar that spring boot creates. To have multiple files you have to rename one of the files to something custom. I named the properties of the core module "core-application.properties".
Then in the API module I added this to the spring boot application class:
#SpringBootApplication
#PropertySource(value = {"core-application.properties", "application.properties"})
Doing this I can correctly use the base properties file and overwrite them in the more specific modules. Also you can still create profile-specific properties file (core-application-production.properties) with this setup, no need to add those to the propertysource manually). Note that #PropertySource does not work for yaml configuration files at this moment.
there is one effective application.properties per project. you just keep 2 properties file for a success build.
when api module use core module, the application.properties in core module is overwrite by api.
Your API's pom.xml must has dependency of CORE module.
the solution is to define properties files as a value of #PropertiesSource in Starter class.
but it is beter to put "classpath:" behind the properties files.
for example in Intellij idea after adding the "classpatch:" word berhind the files name, values become to link. like this:
#SpringBootApplication
#PropertySource(value = {"classpath:core-application.properties", "classpath:application.properties"})
I hope to helped you.

How to test not finding files on classpath? java (spring #Configuration)

I would like to test that a spring
#Configuration class
can handle missing files on the classpath. E.g. when using PropertySourcesPlaceholderConfigurer. But this is just a specific example, the question is really about how to test classes that interact with the classpath (e.g. read a file located in src/main/resources in a maven project).
So in essence I would like to create a spring context where I control the classpath in the test set up code.
The test needs to be a JUnit test.
Hope below may help you
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath*:/testApplicationContext.xml"})
public class YourTestClass{
you have to create a spring context for your test and you can include the production one into it. you can replace classpath*: with a absolute location.
Regards, Rajib.
This work if it's a maven project:
move the classpath file that you want to test the absence from to a separate pom jar module, and include it wherever needed.
move the classpath test to a separate pom jar module named missing-classpath-file-test, but don't include the module with the file that you want to simulate as missing. I will be missing from the classpath only for that test.
When running missing-classpath-file-test, the file will not be on the classpath, and the error you need to reproduce is achieved.
Concerning the question on the comment bellow, with the class loaders that come with application servers and the one used on a junit test it's not possible to programmatically change the classpath.

Classloader problem with EJB

I'm working on a project which includes persistence library (JPA 1.2), EJB 3 and Web presentation layer (JSF). I develop application using Eclipse and application is published on Websphere Application Server Community Edition (Geronimo 2.1.4) through eclipse plugin (but the same thing happens if I publish manually). When publishing to server I get the following error:
java.lang.NoClassDefFoundError: Could not fully load class: manager.administration.vehicles.VehicleTypeAdminBean
due to:manager/vehicles/VehicleType
in classLoader:
org.apache.geronimo.kernel.classloader.TemporaryClassLoader#18878c7
at org.apache.xbean.finder.ClassFinder.(ClassFinder.java:177)
at org.apache.xbean.finder.ClassFinder.(ClassFinder.java:146)...
In web.xml I have reference to EJB:
<ejb-local-ref>
<ejb-ref-name>ejb/VehicleTypeAdmin</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<local>manager.administration.vehicles.VehicleTypeAdmin</local>
<ejb-link>VehicleTypeAdminBean</ejb-link>
</ejb-local-ref>
EJB project has a reference to persistence project, and Web project has references to both projects. I don't get any compilation errors, so I suppose classes and references are correct.
I don't know if it is app server problem, but I ran previously application on the same server using same configuration parameters.
Does anybody have a clue what might be the problem?
Looks almost like it couldn't find the class manager.vehicles.VehicleType when it was attempting to create/load the class manager.administration.vehicles.VehicleTypeAdminBean.
I've encountered similar problems before. When the class loader attempts to load the class it looks at the import statements (and other class usage declarations) and then attempts to load those classes and so on until it reaches the bottom of the chain (ie java.lang.Object). If it cannot find one class along the chain (in your case it looks like it cannot load VehicleType) then it will state that it cannot load the class at the top of the chain (in your case VehicleTypeAdminBean).
Is the VehicleType class in a different jar? If you have a web module and and EJB module do you have the jar containing the VehicleType class in the appropriate place(s). Sometimes with web projects you have to put the jars in the WebContent/WEB-INF/lib folder or it won't find them.
Are both of these projects deployed separately (ie. two ears? or one ear and one war?) or are they together (ie, one ear with jars and a war inside?). I'm assuming the second given you declared your EJB local?
The jars that you are dependent on also have to be declared in your MANIFEST.MF files in the projects that use it.
I'm kind of running on guesses since I do not know your project structure. Showing that would help quite a bit. But I'd still check on where VehicleType is located with regards to your EJB class. You might find it isn't where you think it is come packaging or runtime.
Thanks #Chris for WebContent/WEB-INF/lib idea ! it works for me by following these steps :
1- Export my EJBs to a JAR (MyEJBs.jar)
2- I created another jar with your_installation_path/IBM/SDP/runtimes/your_version/binCreateEJBStrub.bat via CMD.exe, by executing this command :
createEJBStubs.bat <my_path>/MyEJBs.jar -newfile –quiet
3- A new jar will be automatically created in the same directory as MyEJBs.jar named MyEJBs_withStubs.jar
4- Put your new jar in WebContent/WEB-INF/lib
5- Call your EJBs by :
MyEJBRemote eJBRemote;
InitialContext ic = new InitialContext();
obj = ic.lookup("your_ejb_name_jndi");
eJBRemote = (MyEJBRemote ) PortableRemoteObject.narrow(obj,
MyEJBRemote.class);
eJBRemote = (MyEJBRemote ) obj;
Now you can call your EJBs from another EAR

Categories

Resources