Why aspectj-maven-plugin ignore my AnnotationInheritor.aj file? Am I configured something wrong?
I want to advice ItemRepository#getById with custom annotation:
#Repository
public interface ItemRepository extends JpaRepository<Item, Long> {
// AOP does not work, since autogenerated ItemRepositoryImpl#getById
// won't have #MyAnnotation annotation
#MyAnnotation
public Item getById(Long id);
}
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#Inherited
public #interface MyAnnotation {
}
#Aspect
#Component
public class MyAspects {
#Around("#annotation(MyAnnotation)")
public Object execute(ProceedingJoinPoint joinPoint) {
// This advice works correct when #MyAnnotation is placed on class, I tested.
// The problem is that I have to put #MyAnnotation on interface method
}
}
Spring Data JPA use interfaces and Java annotations are never inherited from interface to subclass (due JVM limitations). To make my advice work with custom annotations there is a little AspectJ trick. So as described at previous referrence, I created AnnotationInheritor.aj file:
package com.vbakh.somepackage.aspects;
// For some reason does not work. WHY?
public aspect AnnotationInheritor {
declare #method : void ItemRepository+.getById() : #MyAnnotation;
}
And add the following configurations to my pom.xml:
<dependencies>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<!-- IMPORTANT -->
<useIncrementalCompilation>false</useIncrementalCompilation>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.9</version>
<configuration>
<complianceLevel>1.8</complianceLevel>
<source>1.8</source>
<target>1.8</target>
<showWeaveInfo>true</showWeaveInfo>
<verbose>true</verbose>
<Xlint>ignore</Xlint>
<encoding>UTF-8 </encoding>
</configuration>
<executions>
<execution>
<phase>process-sources</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.8.10</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
P.S. Is there any way to perform the same logic without *.aj files? Means with *.java files.
I copied your code into an AspectJ project (no Spring or Spring AOP there) in order to test it. I found a few problems:
#Around("#annotation(MyAnnotation)") will not find the annotation because there is no fully qualified class name.
declare #method : void ItemRepository+.getById() : #MyAnnotation; does not match your interface method's signature Item getById(Long id).
MyAspects.execute(..) needs to throw Throwable and of course also return something, such as the result of joinPoint.proceed(). But maybe that was just sloppy copy & paste.
After fixing this, the following MCVE works beautifully:
Helper classes making the project compile:
package de.scrum_master.app;
public class Item {}
package de.scrum_master.app;
public interface JpaRepository<P, Q> {}
package de.scrum_master.app;
import org.springframework.stereotype.Repository;
#Repository
public interface ItemRepository extends JpaRepository<Item, Long> {
Item getById(Long id);
}
package de.scrum_master.app;
public class ItemRepositoryImpl implements ItemRepository {
#Override
public Item getById(Long id) {
return new Item();
}
}
Marker annotation:
package de.scrum_master.app;
import java.lang.annotation.*;
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#Inherited
public #interface MyAnnotation {}
Driver Application:
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
ItemRepository repository = new ItemRepositoryImpl();
repository.getById(11L);
}
}
Aspects:
Just in case you wonder why I added execution(* *(..)) to the pointcut, this is because I wanted to exclude matching call() joinpoints which are available in AspectJ as opposed to Spring AOP.
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class MyAspect {
#Around("#annotation(de.scrum_master.app.MyAnnotation) && execution(* *(..))")
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println(joinPoint);
return joinPoint.proceed();
}
}
package de.scrum_master.aspect;
import de.scrum_master.app.Item;
import de.scrum_master.app.ItemRepository;
import de.scrum_master.app.MyAnnotation;
public aspect AnnotationInheritor {
declare #method : Item ItemRepository+.getById(Long) : #MyAnnotation;
}
Console log:
execution(Item de.scrum_master.app.ItemRepositoryImpl.getById(Long))
VoilĂ ! It works nicely.
If it does not work for you like this you have other issues such as (but not exclusively)
the "auto-generated ItemRepositoryImpl#getById" you mentioned in passing. Whenever and wherever this is generated during the build process, it needs to exist before the aspect is applied to it. In order to analyze this I would need an MCVE on GitHub, though.
whether the target code to weave the aspect into is in the same Maven module as the aspect code. If it is not, you need to change your Maven setup.
I guess it should work, even without AnnotationInheritor
made a small demo, have a look...
demo
Related
I have a custom code generator extending JavaGenerator and it would be very useful if the user using this generator could specify additional information like a list of column names to which the custom generator applies to.
My first though would be to add configuration options to the dependency with mojos.
However this does not seem to work properly because during the build cycle two separate instance are created, once from maven and once from JOOQ.
This is my custom generator:
#Mojo(name="generation")
public class CustomGenerator extends JavaGenerator implements org.apache.maven.plugin.Mojo, ContextEnabled {
#Parameter(property="tableNames")
private List tableNames;
private static final Logger log = LoggerFactory.getLogger(CustomGenerator.class);
#Override
protected void generateSchema(SchemaDefinition schema) {
//custom code generation based on the variable "tableNames"
}
#Override
public void execute() throws MojoExecutionException, MojoFailureException {
//called when maven instantiates this class
}
//bunch of empty methods I do not care about but have to be there because
//I cannot let this class also extend from AbstractMojo
#Override
public void setPluginContext(Map map) {
}
#Override
public Map getPluginContext() {
return null;
}
#Override
public void setLog(Log log) {
}
#Override
public Log getLog() {
return null;
}
}
And this is my pom of the project where I use the generator and want to supply additional information:
<plugin>
<groupId>org.jooq</groupId>
<artifactId>jooq-codegen-maven</artifactId>
<version>3.15.4</version>
<configuration>
<generator>
<name>org.example.CustomGenerator</name>
</generator>
</configuration>
</plugin>
<plugin>
<groupId>org.example</groupId>
<artifactId>customgenerator</artifactId>
<version>1.0</version>
<configuration>
<tableNames>
<tableName>test_table</tableName>
</tableNames>
</configuration>
<executions>
<execution>
<goals>
<goal>generation</goal>
</goals>
</execution>
</executions>
</plugin>
...
<dependency>
<groupId>org.example</groupId>
<artifactId>customgenerator</artifactId>
<version>1.0</version>
</dependency>
If there are any other methods to supply custom information to the generator, please let me know.
Using database properties
Maybe, much simpler, use the properties available in database as follows:
<configuration>
<generator>
<database>
<properties>
<property>
<key>some_key</key>
<value>some_value</value>
</property>
</properties>
</database>
</generator>
</configuration>
There's no specification what you place as key/value in those properties. They've been added for purposes like yours, e.g. to add custom configuration to advanced <database> implementations like:
DDLDatabase
XMLDatabase
LiquibaseDatabase
JPADatabase
See also those sections to see how the properties are used by those databases.
Your custom JavaGenerator logic can then access the properties via:
Properties properties = definition.getDatabase().getProperties();
Where definition is any object that is being generated, e.g. SchemaDefinition in your code.
Simplest solution
Of course, setting system properties is always a pragmatic option.
I am building a back-end rest api with spring boot.
Entity:
#Entity
public class Club {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#NotNull
#Column(unique=true)
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Repository:
#RepositoryRestResource
public interface ClubRepository extends JpaRepository<Club, Long>, JpaSpecificationExecutor<Club> {
}
This alone exposes a rest endpoint at http://host/clubs, great. Now, I'd like to allow some parameters in the url for search purposes, so I started following the instructions of http://www.baeldung.com/rest-api-search-language-spring-data-specifications.
But they end-up creating a custom #Controller to pass the request params:
#Controller
public class ClubController {
#Autowired
private ClubRepository repo;
#RequestMapping(method = RequestMethod.GET, value = "/clubs")
#ResponseBody
public List<Club> search(#RequestParam(value = "search") String search) {
/* ... */
return repo.findAll(spec);
}
}
So you see, they end up calling the findAll method of the repository, just passing a specification they build based on the query parameter. Easy enough, but really I'd love to not have to create additional controllers for each of my domain objects. In other words, is there a way to provide this search feature by directly annotating (for example) the #Entity, or overriding methods (like the findAll method) in the repository?
A better approach is to use QueryDsl rather than the criteria API. Spring Data and, by extension, Spring Data Rest will then give you a lot of excellent functionality 'for free' other than some basic configuration.
https://spring.io/blog/2015/09/04/what-s-new-in-spring-data-release-gosling#spring-data-rest
https://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/
One configured your repository interface simply becomes:
#RepositoryRestResource
public interface ClubRepository extends JpaRepository<Club, Long>, QueryDslPredicateExecutor<Club> {
}
An endpoint can then be queried using a dynamic combination of parameters:
e.g.
http://host/clubs?name=someName
http://host/clubs?name=someName&otherProperty=X&sort=name,desc
No further code is required for this i.e. no custom controller, no specifications and no query methods.
For configuring Maven simply add the following to the POM in the module containing your entities: this will generate the necessary 'query' classes.
<dependencies>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<version>${querydsl.version}</version>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources/java</outputDirectory>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
I am trying to create a timer aspect for measuring methods run time.
I created an annotation named #Timer:
#Retention(RetentionPolicy.RUNTIME)
#Target(value = {ElementType.METHOD, ElementType.TYPE})
public #interface Timer {
String value();
}
And then I created the aspect as follows:
#Aspect
public class MetricAspect {
#Autowired
private MetricsFactory metricsFactory;
#Pointcut("#annotation(my.package.Timer)")
public void timerPointcut() {}
#Around("timerPointcut() ")
public Object measure(ProceedingJoinPoint joinPoint) throws Throwable {
/* Aspect logic here */
}
private Timer getClassAnnotation(MethodSignature methodSignature) {
Timer annotation;
Class<?> clazz = methodSignature.getDeclaringType();
annotation = clazz.getAnnotation(Timer.class);
return annotation;
}
I have a configuration class as follows:
#Configuration
#EnableAspectJAutoProxy
public class MetricsConfiguration {
#Bean
public MetricAspect notifyAspect() {
return new MetricAspect();
}
}
Everything up until here is defined in a packaged jar which I use as a dependency in my spring boot application
In my spring boot application I import the MetricsConfiguration and I debugged the code and saw that the MetricAspect bean is created.
I use it in code as follows:
#Service
public class MyService {
...
#Timer("mymetric")
public void foo() {
// Some code here...
}
...
}
But my code doesn't reach to the measure method. Not sure what I'm missing.
For completing the picture, I have these dependencies in my pom file added:
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.7.4</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.7.4</version>
</dependency>
</dependencies>
That's the #Configuration class that imports MetricsConfiguration:
#Configuration
#EnableAspectJAutoProxy
#Import(MetricsConfiguration.class)
#PropertySource("classpath:application.properties")
public class ApplicationConfiguration {
}
It's loaded with Spring's automagically configuration loading.
can #Component or #Configurable solve your issue?
#Aspect
#Component
public class yourAspect {
...
}
Enable Spring AOP or AspectJ
EDIT:
I created a project to simulate your issue, seems no problem after all. Is it affected by other issue?
https://github.com/zerg000000/spring-aspectj-test
I was unable to reproduce your problem using aspectJ 1.8.8 and spring 4.2.5. Here is my maven multi-module approach with aspect in separate jar.
I modified your code slightly but did not change any annotations. The only thing that might be differ is that I've added org.springframework:spring-aop dependency and defined my entrypoint as follows:
#Import(MetricsConfiguration.class)
#SpringBootApplication
public class Application {
// #Bean definitions here //
public static void main(String[] args) throws InterruptedException {
ApplicationContext ctx =
SpringApplication.run(Application.class, args);
ctx.getBean(MyService.class).doWork();
}
}
I had a similar problem where the aspect was built in a jar library, and the spring-boot application was else where. Turns out that the packages for the spring-boot application and the jar library were different. Due to which Spring was not looking into the package of the library to autowire into the application context.
So, had to include #ComponentScan({"base.package.application.*", "base.package.library.*"}) in the Application.java
If the external jar is Spring boot starter, you can config Aspect bean in AutoConfiguration:
(1)
#Aspect
public class MyAspect {
//....
}
(2)
package a.b.c
#Configuration
public class MyAutoConfiguration {
#Bean
MyAspect myAspect() {
return new MyAspect();
}
}
(3)config in spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
a.b.c.MyAutoConfiguration
If the external jar is not a Spring boot starter, just load Aspect as a bean, you can use #ComponentScan
Add this componentScan to resolve the issue.
#ComponentScan("package.of.aspect")
#Configuration
#EnableAspectJAutoProxy
#Import(MetricsConfiguration.class)
#PropertySource("classpath:application.properties")
public class ApplicationConfiguration {
}
Debugging spring-boot Aspectj aspects when pointcut itself has problems is not easy even with detailed logging: How to debug Spring AOP
Unfortunately, spring boot + spring-aop with annotations don't have many ways to debug aspects and why some classes, especially non-spring compoment jar classes, are not scanned, such as jar classes whose methods are in abstract classes or static final methods need the right pointcuts to work covering all classes/implementations even if they are component scanned.
The best way to debug an Asepct (or use core AOP and avoid spring-aop) is to enable aop.xml with configuration control using org/aspectj/aop.xml or META-INF/aop.xml, using the LTW aspectj weaver
-Daj.weaving.verbose=true -javaagent:~/.m2/repository/org/aspectj/aspectjweaver/1.9.5/aspectjweaver-1.9.5.jar
To debug all aspects/classes with Debug/Verbose logs to see why some classes are not being scanned:
...
this almost always helps figuring out the problem with the pointcut or class not getting picked.
Or, just use LTW aop, see, https://github.com/dsyer/spring-boot-aspectj
Adding
ComponentScan(basePackages = "com.github.something.annotation")
basePackages is the package where your aspect resides.
This solution work for me.
You need put #ComponentScan on MetricsConfiguration, as #Configuration will not automatically scan and load component.
I have tested, and it worked!
according to mojohaus explaination, you have to add build settings like below to Woven your aspect into all classes implementing your aspect interface.
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.11</version>
<configuration>
<complianceLevel>1.8</complianceLevel>
<includes>
<include>**/*.java</include>
<include>**/*.aj</include>
</includes>
<aspectDirectory>src/main/aspect</aspectDirectory>
<testAspectDirectory>src/test/aspect</testAspectDirectory>
<XaddSerialVersionUID>true</XaddSerialVersionUID>
<showWeaveInfo>true</showWeaveInfo>
<aspectLibraries>
<aspectLibrary>
<groupId>your aspect groupId</groupId>
<artifactId>your aspect artifactId</artifactId>
</aspectLibrary>
</aspectLibraries>
</configuration>
<executions>
<execution>
<id>compile_with_aspectj</id>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile_with_aspectj</id>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.runtime.version}</version>
</dependency>
<dependency>
<groupId>your aspect groupId</groupId>
<artifactId>youar aspect artifactId</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
I have a data object (with getters\setter only) that needs to be aware of the Spring profile, i.e.
#Value("${spring.profiles.active}")
private String profile;
I added a logic to one of it's 'set' method that checks the profile, i.e.
public void setItem(Item msg) {
if (environmentProperties.isDevMode()) {
this.msg= msg;
}
}
since this class is often marshal\unmarhsalled externally, so, of course the #Value isn't being populated - sine I didn't use spring Autowire to create the class instance... I tried defined the class as component, and autowire to an external class that holds the profile #Value - but it doesn't work
I use spring 3.2 - with no XML definition.
any suggestions?
b.t.w.
that data-objects often wrapped inside an exception class - so when it's created the profile should also be known to the data-object...
thanks!
EDITED:
using ApplicationContextAware doesn't work - I get null the 'setApplicationContext' method is never invoked.
also trying to get context directly doesn't work - get null instead when using:
'ApplicationContext ctx = ContextLoader.getCurrentWebApplicationContext();'
FIXED:
I've eventually found an example how to access the context staticly from an external class:
#Configuration
public class ApplicationContextContainer implements ApplicationContextAware {
private static ApplicationContext CONTEXT;
/**
* This method is called from within the ApplicationContext once it is
* done starting up, it will stick a reference to itself into this bean.
*
* #param context a reference to the ApplicationContext.
*/
#Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
CONTEXT = context;
}
/**
* This is about the same as context.getBean("beanName"), except it has its
* own static handle to the Spring context, so calling this method statically
* will give access to the beans by name in the Spring application context.
* As in the context.getBean("beanName") call, the caller must cast to the
* appropriate target class. If the bean does not exist, then a Runtime error
* will be thrown.
*
* #param beanName the name of the bean to get.
* #return an Object reference to the named bean.
*/
public static Object getBean(String beanName) {
return CONTEXT.getBean(beanName);
}
If I understand you correctly you want to inject into Objects not managed by Spring, but created by some other code that internally calls new and returns objects e.g. a serialization framework.
To inject unmanaged Objects you will need to configure either load-time or compile-time weaving. Load-time weaving requires an agent argument and lib when you start your VM, some containers might do this for you.
Compile-time weaving requires the use of the AspectJ compiler.
Below you will find a complete example using Maven and Spring-Boot:
E.g. run it with:
mvn spring-boot:run -Drun.arguments="--spring.profiles.active=dev"
DemoApplication.java:
package com.example;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.aspectj.EnableSpringConfigured;
import org.springframework.stereotype.Component;
#SpringBootApplication
public class DemoApplication {
#EnableSpringConfigured
#ComponentScan("com.example")
public static class AppConfiguration {
#Value("${spring.profiles.active}")
String profile;
#Bean
public String profile() {
return profile;
}
}
#Configurable
public static class SomePojo {
#Autowired
private String profile;
public void print() {
System.out.println(this + "\t" + profile);
}
}
#Component
public static class Runner {
public void run() {
new SomePojo().print();
new SomePojo().print();
}
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args).getBean(Runner.class).run();
}
}
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.3.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.8</version>
<configuration>
<complianceLevel>1.8</complianceLevel>
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
</configuration>
<executions>
<execution>
<id>compile</id>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
From your description, you're trying to inject the property into POJO, which is used in marshalling. With this structure you may look for different workarounds with static-based/any other complex solutions.
I'd suggest to separate the bean which is used as POJO from the logic which depends on the property value. You can extract that logic to BeanService (which can be placed to Spring context) and handle it on that level, so that you separate the responsibility between Service and Data layers.
You're doing it wrong. Your code does not need to be aware of the profile. In your example, create a Message interface, and a number of bean implementations of this interface, one for each profile, each containing an appropriate message for that profile, and assign each one to a profile so that the bean is instantiated for that profile, and inject the instance into the class that needs the message.
So,
public interface Message { String getMessage(); }
#Profile("dev") #Component
public class DevMessage implements Message {
public String getMessage() { return "this is the dev message"; }
}
#Profile("prod") #Component
public class ProdMessage implements Message {
public String getMessage() { return "this is the production message"; }
}
If you prefer to describe your beans in your #Configuration class, you can mark a whole configuration with an #Profile, and have multiple configurations.
If you inject the Message instance into a class, you can call getMessage() on it. The profile will ensure that you have the appropriate implementation for your environment.
Edit:
I've just reread your question and realised that I've got this wrong. You have entity objects stored outside the application and instantiated through some code/framework. These aren't spring components, and so can't use the spring approach to dependency injection. In this case, don't use spring for them -- it doesn't work, doesn't have to work, and shouldn't work. If you haven't instantiated the object through spring, then it should have nothing to do with spring. I don't know your problem domain, but I've been using spring since it was invented and have never ever had to do this.
I configured my project and wrote my test class for a multi-module project. A test for a business service call to an AS400 server is written with Arquillan, which gives me an InvalidEnvironnmentException when I run the test.
package com.my.company.theproject.business.service.protect;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import javax.inject.Inject;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.ArchivePaths;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.jboss.shrinkwrap.resolver.api.maven.Maven;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import com.my.company.theproject.common.dataaccess.PojoService;
import com.my.company.theproject.ServiceContext;
#RunWith(Arquillian.class)
public class SampleServiceTest
{
#Inject
#PojoService
SampleService service;
private ServiceContext context;
#Before
public void setUp()
throws Exception
{
context = new ServiceContext();
}
#Deployment
public static WebArchive createDeployment()
{
return ShrinkWrap
.create(WebArchive.class)
.addClasses(SampleService.class)
.addAsWebInfResource("META-INF/beans.xml", ArchivePaths.create("beans.xml"))
.addAsLibraries(
Maven.configureResolverViaPlugin().importRuntimeDependencies().resolve().withTransitivity().asFile());
}
#Test
public void test()
{
List<String> list = new ArrayList<String>();
list.add("AAA");
service.getStoredProcedureParameters(context, list);
fail("To be implemented");
}
}
I have written down the imports here, in case some were wrong (e.g. javax.inject.Inject ?).
The problem here is that Arquillian gives me this error at test runtime (first exception of the stack trace) :
java.lang.RuntimeException: Could not invoke deployment method: public
static org.jboss.shrinkwrap.api.spec.WebArchive
com.sopra.banking.packbanque.business.service.protect.SampleServiceTest.createDeployment()
At the end of the stack trace, I see this :
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.jboss.arquillian.container.test.impl.client.deployment.AnnotationDeploymentScenarioGenerator.invoke(AnnotationDeploymentScenarioGenerator.java:177)
... 50 more
Caused by: org.jboss.shrinkwrap.resolver.api.maven.InvalidEnvironmentException: Configuration from environment requires that user has following properties set, however they were not detected in runtime environment:
maven.execution.pom-file
maven.execution.offline
maven.execution.user-settings
maven.execution.global-settings
maven.execution.active-profiles
You should enable ShrinkWrap Maven Resolver Plugin to get them set for you automatically if executing from Maven via adding following to your <build> section:
<plugin>
<groupId>org.jboss.shrinkwrap.resolver</groupId>
<artifactId>shrinkwrap-resolver-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>propagate-execution-context</goal>
</goals>
</execution>
</executions>
</plugin>
at org.jboss.shrinkwrap.resolver.impl.maven.task.ConfigureSettingsFromPluginTask.execute(ConfigureSettingsFromPluginTask.java:71)
at org.jboss.shrinkwrap.resolver.impl.maven.ConfigurableMavenResolverSystemBaseImpl.configureViaPlugin(ConfigurableMavenResolverSystemBaseImpl.java:119)
at org.jboss.shrinkwrap.resolver.api.maven.Maven.configureResolverViaPlugin(Maven.java:77)
at org.jboss.shrinkwrap.resolver.api.maven.Maven.configureResolverViaPlugin(Maven.java:59)
at com.sopra.banking.packbanque.business.service.protect.SampleServiceTest.createDeployment(SampleServiceTest.java:74)
... 55 more
The problem is that this plugin declaration is already in my Maven pom.xml !
<build>
<plugins>
<plugin>
<groupId>org.jboss.shrinkwrap.resolver</groupId>
<artifactId>shrinkwrap-resolver-maven-plugin</artifactId>
<version>2.2.0-beta-2</version>
<executions>
<execution>
<goals>
<goal>propagate-execution-context</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
What is the problem here ?
I think the problem could be that you need to load the pom file first and then resolve as of now it dosent know that whene to resolve the dependencies from..
I am not sure but you can try this... :)
You can find everything about shrikwrap resolver here :
https://github.com/shrinkwrap/resolver