component-scan classpath subdirectory (a'la plugins + annotation capable) - java

Goal:
List item
I have a spring webapp project, which is using component scan for configuration and autowiring interface implementation through maven submodules
the main project is dependant on the other modules thus the jar's of the submodules are placed in the /WEB-INF/lib folder
the submodules (aka plugins) have common package parent name x.y.z.extension eg. x.y.z.extension.pluginA
the classes in this package are annotated with #Component or #Configuration
in the servlet xml configuration i have placed such component-scan information:
Code:
<context:spring-configured />
<context:component-scan base-package="x.y.z.extension" />
With the mentioned configuration everything is working correctly.
What I would like to achieve:
List item
remove dependency of the main webapp maven module and other modules - the core webapp will be shipped without plugins
create subfolder e.g. "WEB-INF/classes/plugins" in the classpath classes dir
put there the mentioned jar's from submodules (or extract the jar content to eg. WEB-INF/classes/plugins/pluginA) - this could be done during "plugin installation" with webapp restart after new plugin installation
spring automagically should detect annotated classes and load it into the application context (and use not annotated classes in the plugin jar (annotated classes are mainly interface implementation but they are using some not annotated classes in the jar))
and ... of course this does not work The classes are not found.
If it possible to achieve this only using spring ecosystem, or should I take a look into other examples e.g. jspf ?
How can I modify classpath scanning with spring and also keep automatic component scanning ?
thanks !

Related

External jar as Spring application

Let say I have an external jar named data-access-0.0.1.jar that contains Spring annotation like #Component, #Bean. But this jar does NOT contain the main method to run as a Spring application (means no #SpringBootApplication, no #ComponentScan, ...).
Now I have another jar named employee.0.0.1.jar (does have the main method to run as Spring boot application - #SpringBootApplication), that use data-access-0.0.1.jar as a dependency. But somehow it does not scan #Bean, #Component in an external jar (error when starting the app, no bean with type "myComponent" found).
I think #ComponentScan in employee-0.0.1.jar should configure base packages include a package from the external jar and it should work, but I do not want to apply this mechanism. I want to somehow configure in the external jar so that any another jar that depend on it should scan the whole jar for autowiring

Valid project structure in EAR

I have an application packed in .ear file. This is the structure:
EmployeeManager
|---EmployeeManagerEJB.jar
---src/ (1)
|---EmployeeManagerJSF.war/
---index.xhtml
---WEB-INF/
---classes/ (2)
---lib/
|---lib/
---EmployeeManagerIntegration.jar
---src/ (3)
I use CDI to inject to classes located in (2) EJBs which interfaces are in (3) and implementations are in (1). However CDI throws exception " Unsatisfied dependencies for type ...". I assume something is wrong with my EAR structure. What should I do to fix this thing up?
CDI does not work by default between sub-deployments as they do not use the same classloader.
You could try placing your CDI beans in the EAR/lib folder this may work
make sure all sub-deployments have beans.xml in them if they are using CDI
there is no reason for this to be an EAR deployment, you can put both jars in WAR/lib and things will work fine
You can define inter-sub-deployment dependencies by adding class-path values into meta-inf/manifest.mf (This is somewhat standard between containers, here's the wildfly example: https://docs.jboss.org/author/display/WFLY8/Class+Loading+in+WildFly)
Or you can rely on your container implementation and turn off sub-deployment isolation (For wildfly see the following: https://docs.jboss.org/author/display/WFLY8/Class+Loading+in+WildFly)
You could copy your CDI beans to each sub-deployment that use them

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.

Resolve Spring injections for dependant jar in base Project

I am working on a project where we developed various jars as small modules. Then these jars were added as dependencies in a base project.
In the base projects applicationContext we have imported the applicationContext of all dependendant jars using the following line
<import resource="classpath*:applicationContext.xml" />
The above line makes sure that all the applicationContexts.xml s get loaded. On code analysis I found that each of the module in its applicationContext has the dataSource injected. Although all the jars are going to use the same datasource still each of the jar is instantiating its own datasource. Is there a way by which I can specify a global datasource which can be injected through base class itself.
In each of the jars we have a datasource injected which the jar uses to perform database operations. I would want the jars to use a datasource of base jar instead of its own datsources.

maven - separate modules for interfaces and implementation with Spring

We are working on Mavenizing our java project and we would like to setup a clean separation between interfaces and implementations for each module.
In order to do so, we want to split each module into two sub-modules one for interfaces and data objects used by them and another for implementations.
For example:
+commons
+commons-api
+commons-impl
The POMs of the modules will be configured such that no module depends on the impl sub-modules. This way no code from one module will be able to "see" implementation details of another module.
What we are having trouble with, is where to put our spring XMLs.
In our project we automatically import spring XML files using wildcard import like
<import resource="classpath*:**/*-beans.xml"/>
This way the location of Spring XMLs doesn't really matter at runtime, as all the modules get loaded into the same class loader and, the strict one way dependency rules in the POMs don't apply.
However, during development we want the IDE - we use Intellij IDEA - to recognize implementation classes referenced from the spring XMLs.
We also want IDEA to recognize beans defined in other modules.
If we put the spring XMLs in API sub-modules - they won't "see" the implementation classes in the impl sub-modules.
If we put them in the impl sub-modules, their beans won't be "seen" from other modules.
It is probably possible to configure the IDEA project to recognize spring XMLs from modules on which there is no dependency, but we prefer for our POMs to hold all the project structure information and not rely on IDEA project files.
We considered creating a third sub-module just to hold Spring XMLs (and perhaps hibernate xmls as well). For example:
+commons
+commons-api
+commons-impl
+commons-config
The external modules will depend on both commons-api and commons-config and commons-config will depend on both commons-api and commons-impl, with the dependency on commons-impl marked as "provided" (to prevent transitive resolution).
This however seems like a complex and awkward solution and we feel that there must be a better - simpler way to achieve interface/impl separation with Maven and Spring.
What you need is a runtime dependency scope:
runtime - This scope indicates that the dependency is not required for compilation, but is for execution. It is in the runtime and test classpaths, but not the compile classpath.
(https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html)
Define a runtime dependency from one impl module to another impl module where you use the impl classes in the *-beans.xml config. Intellij will correctly recognize this in spring configuration files, but won't auto complete them in code (but it will do that in test code).
Also if anyone used the classes in the code, compilation through maven would fail, because the runtime dependency is not on a compile class path.
You can achieve decoupling of api and impl like this:
+ commons (pom)
+ pom.xml <--- serves as a parent aggregator (see below)
+ commons-api (jar) <--- contains models, interfaces and abstract classes only
+ commons-impl (jar) <--- depends on commons-api
+ commons-config (jar) <--- depends on commons-impl only (no need to depend on commons-api as it is brought in transitively)
+ external-project (war or jar) <--- has commons-config as a dependency
Parent aggregator pom (specify build order):
<modules>
<module>commons-api</module>
<module>commons-impl</module>
<module>commons-config</module>
</modules>
The config module can be omitted if it only contains spring application context configuration. The app configuration xml should be in the classpath and folder structure of the module that contains the artifact that you are deploying. So if you are building a war artifact, the app context should be in there.
The only configuration that should be in your commons module would be in a test package of your impl module.
In short you want Idea to override maven dependency graph but avoid keeping this configuration in idea project files?
One option is to group implementation dependencies in a maven profile. This profile would not be enabled by default but you should be able to mark it as active under idea.
Two ideas come to mind:
You will have one (or more) modules where all the modules (api+impl) are dependencies, you could place your spring configuration files there.
Place the spring configuration files in the api modules and declare a dependency on the impl module with scope provided this way the implementations will be known, while there is no dependency of the api for the deployment.
commons-impl at runtime scope in external modules
commons (pom dependencyManagement) =>
+commons-api (compile)
+commons-impl (compile)
+commons-config (compile)
commons-impl (pom dependencies) =>
+commons-api (compile)
+commons-config (compile)
external modules (pom dependencies) =>
+commons-impl (runtime)
+commons-api (compile)
+commons-config (compile)
keep modules number as little as possible;
This speeds up project build time and simplifies its layout.
keep modules structure as plain as possible: single root + all sub modules in the same folder, e. g.:
pom.xml
commons-api/
commons-runtime/
module-a-api/
module-a-runtime/
...
This simplifies navigation across the project, when modules number is really high (>50)
provide runtime-scoped dependencies to the runtime modules only when they are required;
This keeps your architecture clear. Use mocks instead of explicit dependency to another runtime module.
keep your api spring contexts in api modules, define your public beans as abstract bean + interface;
keep your implementation contexts in runtime modules, override api beans with your implementations via spring profiles (use <beans profile="default").
Result: simple, transparent layout and design; full ide support; no explicit dependencies on runtime module internals.

Categories

Resources