How can I include a resource file (logback.xml) in a project when building JAR but not when embedded in a WAR? - maven

I'm working on a project that uses two maven projects (named core and webapp); core is built with JAR packaging and used for two different purposes: as a stand-alone app (essentially an executable JAR), and also embedded into webapp.
For its purpose as a stand-alone app, core needs to have its own logback configuration (a logback.xml file) that needs to be included on the classpath. Normal Maven convention would have me put it in src/main/resources/logback.xml. That works fine, but causes a problem when the core JAR is included in webapp. webapp needs to have its own logback configuration, but the container (tc Server or Jetty) is picking up the one from core.jar first.
I realize that logback can be told about a custom config location via a system property (-D on the command line) but that's not viable in a app container like Tomcat or Jetty.
I've read some other people asking about this situation, but none of the solutions I've seen sits well with me. One solution involved setting up a context listener that runs early in the webapp initialization and explicitly configures logback based on a <context-param>. That's a bit brutish in my opinion, and probably a hard sell to my fellow dev team when log4j "just works" in this situation.
I'm far from a Maven expert, so I'm hoping there is some elegant way to get Maven to help me here. Or perhaps some logback extension or add-on that makes it more web-app friendly. Or even a clever idea that I haven't thought of.

There are a number of possible solutions, but the easiest is to put the file in its own module and mark the dependency as provided. The, conspire to have it on the classpath when running the standalone version of the app.

The solution that we ended up using was to leave only the common "non-app" pieces (code and configuration) in core and then extract the other "app" pieces into a new module (batch-app).
The logging configuration only lives in the 2 app projects (webapp and batch-app) that depend on core. core has a logback-test.xml configuration in it, but that's excluded from the JAR that maven builds (since it's in the src/test/resources folder).

Related

Log4J2 with Websphere 8.5

I am trying to do a log4j migration for a legacy application. I made the changes to the configuration files and tested it locally with Tomcat, where the logs were displayed correctly. However when I deploy on WAS, the logging stops.
I checked a lot of pages about the necessary dependencies and the possible problems, but now I feel I'm stuck. Here is what I already tried:
Added the log4j jars manually to the lib/ext directory in WAS (together with commons-logging, I read an article where it helped, although all these jar-s were already present in the ear)
Double checked, that the configuration file is on the classpath and that it's syntax is correct
Added logj42-web dependency
Added the necessary filters in web.xml (Spring version is 3.8, so also the config listener with the config name context-param)
Checked classloader in WAS - it's parent first
Am I missing something?
Any ideas are appreciated. Thank you in advance!
WAS includes its own commons-logging API (that does not utilize Log4j), so bringing your own logger requires a bit of extra class-loader-related config. Typically, that would go something like this:
Put your commons-logging and log4j jars, along with your logging properties files, in some directory (not WAS_HOME/lib/ext) readable by the user running the server.
Create a shared library on the server, with that directory as its class path, and select the "use an isolated class loader" option. Associate that shared library with your application or web module.
Ideally, that should be it. Isolated shared libraries search themselves before delegating to the server's loaders, so your application will "see" the commons-logging/log4j classes in the shared library instead of in the server, and likewise, it should pick up the configuration files from that directory instead of the ones found in the server.
You can also accomplish this same basic thing by leaving all the logging stuff in your WAR or EAR and setting its class loader to parent-last (which causes the class loader to search locally before delegating to the server-level loaders), but that is a bit riskier configuration - if your application includes APIs that are also provided by the server, parent-last class loading increases the possibility of ClassCastExceptions or LinkageErrors.

Spring Boot Multi Module and Fat jar with Shared Features

Experts,
I need some expert advice on how to approach the below use case in spring boot.
I need to have a maven multi-module approach to my project.
I need to have a single jar as output of the final build process.
There are to be common modules for controllers, data access and other functionality
Other modules are to be created based on functionality domain for eg a module for Payroll, a module for Admin etc etc.
Each domain functional module will then have their own controllers extending the common controller, exception handler and so on.
Each module will also have its own set of thyme leaf pages.
The reason for following such an approach is we have development in phases and we will be rolling out based on functional modules.
Here are the issues that I can sense using this approach.
Where do I add the spring web dependency? If I add to the parent pom - it gets replicated across the children and there will be port conflict issues as each module loads. the same issue will also be there the moment I add it to two child modules.
How do I build the fat jar which has all the jars from all modules and works as the final deployment?
All the text that I read i can't see anything even close to what I am trying to achieve.
AD1. They will not unless you are trying to setup independent application context in each module. Of course you can do that(it might be complicated but I believe it's achievable), but for me it's an overkill. Personally I think it's better to have one application context and rely on scanning components that are present in classpath.
AD2. The structure in maven might be a little bit complicated and overwhelming at first glance but it makes sense. Here's how I see it:
Create a parent module that will aggregate each module in project and will declare library/plugin dependencies for submodules.
Create 1-N shared submodules that will be used in other modules. With come common logic, utils, etc.
Create 1-N submodules that will be handling your business logic
Create an application submodule that creates application context and loads configuration and components from classpath
Create a submodule that will be responsible for packaging process, either to war, jar, uber-jar or whatever else you desire. Maven jar plugin should do that for you. For executable uber-jar, you have dedicated tool from spring.
Now you can choose three ways(these ways I know) of loading your modules.
1. Include some modules in maven build based on the build configuration via maven profiles and let spring IoC container load all the components he finds in the classpath
2. Include all of the modules in maven build and load them depending on spring active profiles - you can think about it as of feature flag. You annotate your components or configuration class with #Profile("XYZ") telling spring IoC container whether to instantiate component or not. You will need (most flexible solution) to provide a property file which tells spring which profiles are active and thus which modules should be loaded
3. Mix of these two above.
Solution 1 pros:
build is faster (modules that are not included will be skipped during build)
final build file is light (modules that are not included are... not included ;))
nobody can run module that is not present
Solution 1 contras:
project descriptor in maven may explode as you might have many different profiles
Solution 2 pros:
it's fairly easy and fun to maintain modules from code
less mess in project descriptor
Solution 2 contras:
somebody can run module that is not intended to be run as it's present in classpath, but just excluded during runtime via spring active profiles
final build file might be overweight - unused code is still present in code
build might take longer - unused code will be compiled
Summary:
It's not easy to build well structured project from scratch. It's much more easier to create a monolith and then split it into modules. It's because if you already created a project, you've probably already identified all the domains and relations between them.
Over past 8 years of using maven, I honestly and strongly recommend using gradle as it's far more flexible than maven. Maven is really great tool, but when it comes to weird customization it often fails as it's build capabilities rely on plugins. You can't write a piece of code on the fly to perform some custom build behaviour while buidling your project, you must have a dedicated plugin for doing that. If such plugin exists it's fine, if it's not you will probably end up writing your own and handling its shipment, so anyone in your company can easily perform project build.
I hope it helps. Have fun ;)

WebSphere 7 org.apache.axis2.deployment.WarBasedAxisConfigurator <init> org.apache.commons.fileupload.FileUploadException

This is a strange problem. I have an existing JEE project that is and has been in production on WebSphere 7. It is a multi-module EAR file. It has 2 WARs and 1 shared Java project. Most of it is Spring 4 based, but we have some web services we host using axis2 (1.5.4). This project does not use Maven. It does use a WebSphere deployment.xml (in the EAR) with the warClassloaderPolicy="SINGLE" and the classloader mode="PARENT_LAST". This controls the classloader policies for this EAR. All the jar files that the Java project and the WARs rely on are stored at the EAR level and referenced at the module level via the manifest files. That all works fine and has for several years.
Now I was trying to convert this project to a Maven project after all these years. Initially, I left the deployment.xml file settings as they always have been. But with these settings I was getting:
java.lang.VerifyError at com.sun.xml.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl "org.springframework.oxm.jaxb.Jaxb2Marshaller".
After much googling on this site and all around and trying 50,000 things, I tried one thing, which worked for most of the app. I switched the classloader mode to "PARENT_FIRST" and all of a sudden the WAR that exclusively uses Spring 4 worked. It started up without error and I was able to run it.
But, the other WAR which hosts the axis2 web services started getting the error:
org.apache.axis2.deployment.WarBasedAxisConfigurator <init> org.apache.commons.fileupload.FileUploadException....Caused by: java.lang.NoClassDefFoundError: org.apache.commons.fileupload.FileUploadException....Caused by: java.lang.ClassNotFoundException: org.apache.commons.fileupload.FileUploadException
This class (org.apache.commons.fileupload.FileUploadException) is in both the compile and runtime classpath. In eclipse I can see it in the "Maven Dependencies" library that the m2e eclipse plugin creates from the POM files. I can also see this jar in the WebSphere runtime module classpath inspector (it shows up in there 3 times! Once under each WAR and once just in a list from the Maven repository location.)
One thing of note is that I've configured the project with a parent POM, where most of the dependencies are declared at the parent and then are inherited for each of the modules. In the EAR project, the POM is using the maven-ear-plugin with skinnyWars=true. However, it appears the skinnyWars directive is not respected when the m2e eclipse plugin deploys the EAR into the local WebSphere server (that is evident because I see these jars showing up in the classpath 3 times). But when I create an EAR using the Maven build, the EAR comes out correctly with each jar only showing up in the EAR one time.
This is my last hope to post something here and see if someone has an idea for something to try, otherwise I can't think of anything else to try.
Thanks...
Is there a particular reason you need to run with a single class loader for all the WAR modules, other than convenience? If not, you could rework the application in order to allow you to run PARENT_FIRST in the Spring WAR and PARENT_LAST in the Axis2 WAR.
If that's not an option for some reason, the path of least resistance might be to go back to PARENT_LAST and remove some libraries from the app or module. The VerifyError generally occurs if you're running with PARENT_LAST and have a library in the app that is also present in the server - in this case, the exception looks like it's coming out of JAXB, so you might want to see if simply removing JAXB (and perhaps JAXP, if present, since it's heavily used by JAXB) from the application resolves the issue with your default setup.
You could also stick with PARENT_FIRST and move the Axis2 jars to an shared library with an isolated class loader, associated with the app. An isolated shared library would make just the stuff in the shared library PARENT_LAST, so you can avoid the VerifyError and still get PARENT_LAST for Axis2. The issue in the PARENT_FIRST case appears to be because you're picking up WebSphere's copy of Axis2 but somehow have a dependency inserted from your app's copy - the failing class load is occurring in a server-level loader, so it doesn't see the copy in your app.

Maven Jetty plugin with endorsed external directory

I have a project with more than an hundred external library dependencies, here we use tomcat with this endorsed jar libs configured on a directory in the server (now is under $CATALINA_HOME/lib/endorsed), so the webapp can access those resources on runtime start.
I wanted to try jetty instead, because tomcat takes too much memory and crashes frequently. Now I'm wondering if there is a parameter to pass on maven-jetty-plugin to specify this jar's folder so as the webapp class loader find them in its classpath.
I've tried extraClasspath in configuration tag, but it seems to load only classes and ignore all jars in the directory I set into (if I pass the full name path of the jar, it is loaded, but I don't want to set every library that I need there).
Thanks in advance for the help
update:I know it's not a standard maven operation, i'm searching for an emergency workaround since this project is very huge and I can't refactor as I want.
But also I expected this feature was not as tricky as it seemed to me at first glance.
You need to pass them as absolute paths, or, alternatively, have them as dependencies of the plugin itself.
What you want to have done goes against Maven's portability principles, so don't expect it to support it.

xmlaccess deploy portlet with library reference

I have problem with deploying JSR168 portlet using xmlaccess. I have no problem with deploy and join to conrete page but I would like to add shared library reference automatically. Is it possible?? I added shared library named 'libshared' using IBM WS console. Can I add this reference in input xml using by xmlaccess?
I don't think you can do this in xmlaccess. But you may try putting a reference to the library under the Manifest.MF file of the META-INF directory of your portlet's war file.
Or could just put the shared jar file under your /shared/ext directory. Or you could put it inside your wps.ear file. Mind you, either of these two solutions would share your library with the entire portal installation, rather than just select portlets.
You can deploy the application using wsadmin or similar and use that to update the classpath (i.e. for the shared library), you can then use xmlaccess to deploy the portlets and reference the previously deployed application - although I think this may only work in WebSphere Portal 6.1.
Give me a shout if you need further details.
I encountered this as well, a while ago... and researched it to the max, including spending some time chatting with IBM's support in various levels.
The XMLAccess protocol doesn't provide for such "system-level" configuration alongside Portlet application deployment; it can only be used to install, customize and uninstall Portlet applications and related artifacts.
If your deployment strategy involves deploying WAR files directly through XMLAccess, then you will have to manually add the shared-library to the application through the WAS admin console; this will have to be done manually because, when deploying WAR files through XMLAccess, an EAR with some random name is being created by WebSphere Portal to "host" your WAR file; hence you can't script the attachment of a shared library.
(alternatively, you may wish to add the shared library to the server's (WebSphere_Portal) classpath)
If your deployment strategy, instead, involves deploying Portlet applications packaged as EARs, then you're in a better position; you could automate the shared-library attachment as part of your EAR deployment process, then use XMLAccess to inform WebSphere Portal about the location, in the EAR, of your Portlet applications (which is what Michael mentioned above; it works in WebSphere Portal 6.0 as well).
Good luck.

Resources