Spring boot additional Crash Command - java

According to the Spring boot documentation, it's possible to define additional command when using a remote shell based on Crash.
Default locations for these commands are classpath*:/commands/,classpath*:/crash/commands/
A property can be used to override the default locations but in the provided example, the custom command is located in resources.
In my opinion, custom commands (at least java commands) shouldn't be located in resources but in src/main/java.
It works fine when defining a custom path in resources but how can I define a custom path in src/main/java? Didn't find a way to do it for now!

If they're under src/main/java, they'll be compiled automatically which is not what you need. My solution was to simulate that directory as a resources folder, which in short translates to:
configure the compiler plugin to ignore that particular folder
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<excludes>
<exclude>crash/commands/*</exclude>
</excludes>
</configuration>
</plugin>
copy the files just like any regular resources in the target directory
<resource>
<directory>src/main/java/crash/commands</directory>
<targetPath>crash/commands</targetPath>
<filtering>false</filtering>
</resource>
Minor update & disclaimer:
As you may already know, there are a couple of closures which are executed on login/logout. At least with v1.3.1, which is what I'm blindly inheriting from spring-boot, it will pick the first login.groovy it finds in the classpath. My project's artifact is packaged in an RPM along with all the other dependencies. Since its name begins with r, it comes after crash.shell-1.3.1.jar which is where the defaults reside, so I had to do the following small hack to make it pick up my own scripts instead of the default ones:
<!-- hack to make CRaSH pick up login.groovy from our jar instead of the default one -->
<finalName>0_${project.artifactId}-${project.version}</finalName>

You can try to put your command at src/main/resources/commands/

Related

Maven Resources plugin ignoring exclusion list

TL; DR
Maven's Resources plugin doesn't seem to respect excludes elements in the resource configuration.
Setting
I have a large Java/Dart project where I need to deploy a WAR file that has both my UI and my backend in separate JARs. I want to cut down on the size of the deployed file, and I want to drop certain folders from the WAR. Based on the plugin documentation, I thought I could simply set excludes in my plugin configuration, and it won't copy over the unnecessary folders. However, it seems the Resources plugin is outright ignoring these, despite, the Maven model package including a setExcludes function.
Current Attempts
So far, I've tried two main approaches. My configuration is as follows:
<configuration>
<outputDirectory>${project.build.directory}/${project.build.finalName}</outputDirectory>
<resources>
<resource>
<directory>src/main/webapp</directory>
<excludes>
<exclude>web.xml</exclude>
<exclude>appengine-web.xml</exclude>
<exclude>**/web/_el/*</exclude>
<exclude>WEB-INF/pages/frontend/**</exclude>
<exclude>**/_el/js/frontend/**</exclude>
<exclude>**/_el/dart/app/dashboard/lib/**</exclude>
<exclude>**/_el/dart/app/dashboard/.dart_tool/**</exclude>
</excludes>
</resource>
</resources>
</configuration>
I tried to use this config inside the execution element, as well as outside from directly under the plugin element, but both times it was ignored, and everything in the webapp directory was copied over mindlessly.
On a hunch, I did try setting filtering to true, but that just ate up all the memory in my computer, and it didn't even work - what it did process was copied over.
I also tried using the Shade plugin, but gave up on that pretty quickly, as the DontIncludeResourceTransformer only permits suffix-filtering, which is not adequate for my use case.
Question
So what am I doing wrong? Based on the docs, I believe the plugin should respect my excludes list and skip the vast majority of files, but it's evidently not doing that.
You need to use apache **maven war plugin**.
The WAR Plugin is responsible for collecting all artifact dependencies, classes and resources of the web application and packaging them into a web application archive.
It is possible to include or exclude certain files from the WAR file, by using the and configuration parameters. They each take a comma-separated list of Ant file set patterns. You can use wildcards such as ** to indicate multiple directories and * to indicate an optional part of a file or directory name.
Here is an example where we exclude all JAR files from WEB-INF/lib:
<project>
...
<build>
<plugins>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.1</version>
<configuration>
<packagingExcludes>WEB-INF/lib/*.jar</packagingExcludes>
</configuration>
</plugin>
</plugins>
</build>
...
</project>
https://maven.apache.org/plugins/maven-war-plugin/examples/including-excluding-files-from-war.html

How to set VM options for JLink launcher executable

When using jlink, a bin/java file is generated. This executable will accept VM options by specifying options on the command line in the usual way (such as -Dsystem.property=value or -Xmx1G).
jlink also provides a --launcher option to create an executable that can be run directly, instead of having to invoke the bin/java executable with a module name.
How do I make the launcher executable pre-configured to use my choice of JVM options?
You can use the add-options jlink plugin.
For example, if you want to set Xmx:
jlink --add-options="-Xmx100m" ...
To see a list of jlink plugins, run jlink --list-plugins.
The add-options plugin is currently documented (JDK14) as follows:
Plugin Name: add-options
Option: --add-options=<options>
Description: Prepend the specified <options> string, which may include
whitespace, before any other options when invoking the virtual machine
in the resulting image.
Beware that some of the plugins are, apparently, unstable (including add-options): https://docs.oracle.com/en/java/javase/12/tools/jlink.html
There are one or two ways of going about this,but mostly I'm going to concentrate on the default java ways.
Actual answer - Use JPackage. JLink is just the image of a runtime. JPackage is your distributable
Support for native packaging formats to give the end user a more natural installation experience. Specifically, the tool will support the following formats:
Windows: msi, exe
macOS: pkg, app in a dmg (drag the app into the Applications directory)
Linux: deb, rpm
The application will be installed in the typical default directory for each platform unless the end-user specifies an alternate directory during the installation process (for example, on Linux the default directory will be /opt).
The ability to specify JDK and application arguments at packaging time that will be used when launching the application
The ability to package applications in ways that integrate into the native platform, for example:
Setting file associations to allow launching an application when a file with an associated suffix is opened
Launching from a platform-specific menu group, such as Start menu items on Windows
Option to specify update rules for installable packages (such as in rpm/deb)
1) - Specify an #Args file
You can make an #args file that you can deploy (bundled) with your jlink app, and reference it when starting the application
java #args -m module/main
2) Use the new environment variable
JDK_JAVA_OPTIONS=--add-opens java.base/java.lang=...... -Xmx1G -Djdk.logging.provider=
https://docs.oracle.com/javase/9/tools/java.htm#JSWOR624
3) Use the JLink/JMod to specify main class in module
https://maven.apache.org/plugins/maven-jmod-plugin/plugin-info.html
<plugin>
<artifactId>maven-jmod-plugin</artifactId>
<version>3.0.0-alpha-1</version>
<extensions>true</extensions>
<configuration>
<module>
<mainClass>mainClass</mainClass>
</configuration>
</plugin>
4) Use JLink to create a custom launcher/Edit JDK_VM_OPTIONS
<plugin>
<artifactId>maven-jlink-plugin</artifactId>
<version>3.0.0-alpha-2-SNAPSHOT</version>
<extensions>true</extensions>
<configuration>
<noHeaderFiles>true</noHeaderFiles>
<noManPages>true</noManPages>
<stripDebug>true</stripDebug>
<verbose>true</verbose>
<compress>2</compress>
<launcher>customjrelauncher=module/mainClass</launcher>
</configuration>
<dependencies>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>${maven.asm.version}</version>
</dependency>
</dependencies>
</plugin>
In the generated .sh/.bat there is a variable to specify any custom addon's
You can also specify the main class descriptor in your module-info using moditect if you are using it:
<plugin>
<groupId>org.moditect</groupId>
<artifactId>moditect-maven-plugin</artifactId>
<executions>
<execution>
<id>add-module-infos</id>
<phase>package</phase>
<goals>
<goal>add-module-info</goal>
</goals>
<configuration>
<overwriteExistingFiles>true</overwriteExistingFiles>
<module>
<mainClass>mainClassLocation</mainClass>
</module>
</configuration>
</execution>
</executions>
</plugin>

How to properly use the server stubs generated from a swagger specification?

I'm using Swagger 2.0 and swagger-codegen (actually the swagger-codegen-plugin for Maven) to specify,document and generate an API, with Java as the target language.
The project is already setup to build the server stubs (JAX-RS) and documentation, and Eclipse recognizes the generated code in the project buildPath.
I'm not sure of what is the proper workflow from here. :-/
I don't think I should modify the generated classes, otherwise my changes would be overwritten whenever I change the swagger spec, an I expect it will change as I think more about the API as the development goes on.
What should I do then? Inherit from the generated classes (which ones?) or include them in my own classes?
There are two steps to the solution here.
Add **/*Controller.java or **/*Impl.java to .swagger-codegen-ignore file. Depending on the language used the default implementation is provided in a *Controller.java or *Impl.java file. Once the default implementation is excluded from generation, you can implement the generated interfaces in your own class. The code in your own class will not get refreshed on mvn clean.
.swagger-codegen-ignore file itself is an auto-generated file hence whatever you add in step 1 gets refreshed when you do a mvn clean. To avoid this keep your version of .swagger-codegen-ignore in your resources folder and add the below plugin to your pom, to copy the file at the start of the Maven lifecycle:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-resources</id>
<phase>initialize</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/generated/swagger</outputDirectory>
<resources>
<resource>
<directory>${basedir}/src/main/resources</directory>
<includes>
<include>.swagger-codegen-ignore</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
I believe you will need to update the Impl classes, e.g. PetApiServiceImpl.
If you want to skip certain files (e.g. Impl classes) during code regeneration, you can add the files to .swagger-codegen-ignore.

Maven AppAssembler not finding class

Attempting to modify an existing Java/Tomcat app for deployment on Heroku following their tutorial and running into some issues with AppAssembler not finding the entry class. Running target/bin/webapp (or deploying to Heroku) results in Error: Could not find or load main class org.stopbadware.dsp.Main
Executing java -cp target/classes:target/dependency/* org.stopbadware.dsp.Main runs properly however. Here's the relevant portion of pom.xml:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>appassembler-maven-plugin</artifactId>
<version>1.1.1</version>
<configuration>
<assembleDirectory>target</assembleDirectory>
<programs>
<program>
<mainClass>org.stopbadware.dsp.Main</mainClass>
<name>webapp</name>
</program>
</programs>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>assemble</goal>
</goals>
</execution>
</executions>
</plugin>
My guess is mvn package is causing AppAssembler to not use the correct classpath, any suggestions?
Your artifact's packaging must be set to jar, otherwise the main class is not found.
<pom>
...
<packaging>jar</packaging>
...
</pom>
The artifact itself is added at the end of the classpath, so nothing other than a JAR file will have any effect.
Try:
mvn clean package jar:jar appassembler:assemble
Was able to solve this by adding "$BASEDIR"/classes to the CLASSPATH line in the generated script. Since the script gets rewritten on each call of mvn package I wrote a short script that calls mvn package and then adds the needed classpath entry.
Obviously a bit of a hack but after a 8+ hours of attempting a more "proper" solution this will have to do for now. Will certainly entertain any more elegant ways of correcting the classpath suggested here.
I was going through that tutorial some time ago and had very similar issue. I came with a bit different approach which works for me very nicely.
First of all, as it was mentioned before, you need to keep your POM's type as jar (<packaging>jar</packaging>) - thanks to that, appassembler plugin will generate a JAR file from your classes and add it to the classpath. So thanks to that your error will go away.
Please note that this tutorial Tomcat is instantiated from application source directory. In many cases that is enough, but please note that using that approach, you will not be able to utilize Servlet #WebServlet annotations as /WEB-INF/classes in sources is empty and Tomcat will not be able to scan your servlet classes. So HelloServlet servlet from that tutorial will not work, unless you add some additional Tomcat initialization (resource configuration) as described here (BTW, you will find more SO questions talking about that resource configuration).
I did a bit different approach:
I run a org.apache.maven.plugins:maven-war-plugin plugin (exploded goal) during package and use that generated directory as my source directory of application. With that approach my web application directory will have /WEB-INF/classes "populated" with classes. That in turn will allow Tomcat to perform scanning job correctly (i.e. Servlet #WebServlet annotations will work).
I also had to change a source of my application in the launcher class:
public static void main(String[] args) throws Exception {
// Web application is generated in directory name as specified in build/finalName
// in maven pom.xml
String webappDirLocation = "target/embeddedTomcatSample/";
Tomcat tomcat = new Tomcat();
// ... remaining code does not change
Changes to POM which I added - included maven-war-plugin just before appassembler plugin:
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.5</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>exploded</goal>
</goals>
</execution>
</executions>
</plugin>
...
Please note that exploded goal is called.
I hope that small change will help you.
One more comment on that tutorial and maven build: note that the tutorial was written to show how simple is to build an application and run it in Heroku. However, that is not the best approach to maven build.
Maven recommendation is that you should adhere to producing one artifact per POM. In your case there are should two artifacts:
Tomcat launcher
Tomcat web application
Both should be build as separate POMs and referenced as modules from your parent POM. If you look at the complexity of that tutorial, it does not make much sense to split that into two modules. But if your applications gets more and more complex (and the launcher gets some additional configurations etc.) it will makes a lot of sense to make that "split". As a matter of fact, there are some "Tomcat launcher" libraries already created so alternatively you could use of one them.
You can set the CLASSPATH_PREFIX environment variable:
export CLASSPATH_PREFIX=target/classes
which will get prepended to the classpath of the generated script.
The first thing is that you are using an old version of appassembler-maven-plugin the current version is 1.3.
What i don't understand why are you defining the
<assembleDirectory>target</assembleDirectory>
folder. There exists a good default value for that. So usually you don't need it. Apart from that you don't need to define an explicit execution which bounds to the package phase, cause the appassembler-maven-plugin is by default bound to the package phase.
Furthermore you can use the useWildcardClassPath configuration option to make your classpath shorter.
<configuration>
<useWildcardClassPath>true</useWildcardClassPath>
<repositoryLayout>flat</repositoryLayout>
...
</configruation>
And that the calling of the generated script shows the error is depending on the thing that the location of the repository where all the dependencies are located in the folder is different than in the generated script defined.

How to include external classes in my jar

I have a problem with a service I am trying to write. I am trying to create a service that runs in the background on a windows system but uses java. I have seen several ways of doing this, but decided on one method that seemed to meet my requirements. The service will check a database for items it needs to work on. When it finds an item in the DB that it needs to do it will run some system commands to take care of them.
I found a way to use the tomcat7.exe file to run a jar as a service and that worked pretty well for basic stuff. Anything I write and compile into my jar file "myService.jar" we'll can call it goes well enough. The problem is that we already have several classes written for accessing the DB and running commands that are precompiled in a library of classes called BGLib-1.0.jar.
I have used this library in writing several jenkins plugins and had no problems calling functions from it. They all work fine when I create an hpi file and deploy it in Jenkins. There the compiler (Eclipse using Maven) packages the BGLib jar in with the plugin jar and Jenkins figures out how to get them to see one another.
When I build my service jar, however, it doesn't work when I deploy it.
I run a command like this to install the Tomcat exe renames to myservice.exe:
d:\myService\bin>myService.exe //IS//myService --Install=D:\myService\bin\myService.exe --Description="run some commands
Java Service" --Jvm=auto --Classpath=D:\myService\jar\myService.jar;D:\myService\jar\BGLib-1.0.jar --StartMode=jvm --
StartClass=com.myCompany.myService.myService --StartMethod=windowsService --StartParams=start --StopMode=jvm --StopClass
=com.myCompany.myService.myService --StopMethod=windowsService --StopParams=stop --LogPath=D:\myService\logs --StdOutpu
t=auto --StdError=auto
When I deploy this with code solely within the myService.jar the service behaves as expected, but when I try to call functions within the BGLib-1.0.jar I get nothing. The jvm appears to crash or become unresponsive. Debugging is a little tricky but it looks like I am getting class not found errors.
I tried adding the entry below in the POM file to see if changing the classpath entry in the manifest would help, but it didn't change the manifest. I am still kind of clueless ass to how the manifest file works. Any documentation on that would be cool. I have been to Maven's site and it doesn't seem to have comprehensive documentation on the tags available. Is there something I need to change in the manifest to get my jar to see external classes? Or is there something I can add that will get Maven to compile the classes from that jar in with my jar?
thanks in advance.
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>com.myCompany.myService.myService</mainClass>
<customClasspathLayout>BGLib-1.0.jar</customClasspathLayout>
</manifest>
</archive>
</configuration>
To answer mainly the question of the title, you can the shade plugin to include dependencies into your final jar. You can even even relocate the class files (e.g. change package name) within the final jar so that the included classes don't conflict with different versions of the shaded dependency on the classpath. Not sure if this is the best solution for your particular problem though.
You can use the maven-dependency-plugin unpack-dependencies goal to include the contents of a dependency in the resulting artifact.
An example of how to do this would be:
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>${project.artifactId}-fetch-deps</id>
<phase>generate-sources</phase>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.outputDirectory}</outputDirectory>
<stripVersion>true</stripVersion>
<excludeTransitive>true</excludeTransitive>
<includeArtifactIds>protobuf-java</includeArtifactIds>
</configuration>
</execution>
</executions>
</plugin>
This will expand the protobuf-java dependency (flatten it) and include the contents in the resulting artifact generated by your build.
Looks to me you actually want to use the appassembler-maven-plugin, otherwise I'd go for the maven-shade-plugin.

Categories