Spring 3.1 bean visibility using bean definition profiles - java

I have been experimenting with using Spring 3.1's bean definition profiles and nested beans. I had hoped that I could define different beans depending on the active profile. Consider the following heavily over simplified example such that my Spring context contains something like
<bean id="say" class="test.Say" p:hello-ref="hello"/>
<beans profile="prod">
<bean id="hello" class="test.Hello" p:subject="Production!"/>
</beans>
<beans profile="dev">
<bean id="hello" class="test.Hello" p:subject="Development!"/>
</beans>
I get the following error:
Exception in thread "main"
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'say' defined in class path resource
[applicationContext.xml]: Cannot resolve reference to bean 'hello'
while setting bean property 'hello'; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
bean named 'hello' is defined at
org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:328)
at
org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:106)
at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1360)
aJava Result: 1
I was expecting that the hello bean would be defined according to the active Maven profile (in my case prod or dev). I'm starting to think that the Spring active profiles (spring.profiles.active) may be completely unrelated to Maven profiles.
Could somebody please explain where I am going wrong? (Is this even possible using profiles?).

I was expecting that the hello bean would be defined according to the active Maven profile (in my case prod or dev). I'm starting to think that the Spring active profiles (spring.profiles.active) may be completely unrelated to Maven profiles.
That's true, they are unrelated.
Here is how you can fix it:
Make sure that the web.xml that you have in src/main/webapp/WEB-INF/ folder has the following context setting:
<context-param>
<param-name>spring.profile.active</param-name>
<param-value>${profileName}</param-value>
</context-param>
And then make sure that the maven-war-plugin has filtering turned on for the web.xml:
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.3</version>
<configuration>
<filteringDeploymentDescriptors>true</filteringDeploymentDescriptors>
</configuration>
</plugin>
And then lastly in your profiles:
<profiles>
<profile>
<id>dev</id>
<properties>
<profileName>dev</profileName>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<profileName>prod</profileName>
</properties>
</profile>
</profiles>
You could also add a default value in the normal properties section:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<profileName>dev</profileName>
</properties>
So if you run without the -P option the dev spring profile will be used.
When running mvn package the web.xml will have the correct value for the spring.profile.active.

Thanks to maba (whose answer I shall accept), I started thinking about this in a different way.
I've modified the parent bean "say" because it needs to be lazily initialized because when it is initially encountered the nested bean contexts do not yet exist. So the new version adds a new bean and changes the "say" definition such that it now looks like:
<bean class="test.InitProfile" p:profiles="dev"/>
<bean id="say" class="test.Say" lazy-init="true" p:hello-ref="hello"/>
The new InitProfile bean is an InitializingBean responsible for setting up the active profiles.
It contains:
package test;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.util.StringUtils;
public class InitProfile implements InitializingBean, ApplicationContextAware {
private ConfigurableApplicationContext ctx;
private String[] profiles;
public void setApplicationContext(ApplicationContext ac) throws BeansException {
ctx = (ConfigurableApplicationContext) ac;
}
public void setProfiles(String inprofiles) {
if (inprofiles.contains(",")) {
profiles = StringUtils.split(inprofiles, ",");
} else {
profiles = new String[]{inprofiles};
}
}
public void afterPropertiesSet() throws Exception {
String[] activeProfiles = ctx.getEnvironment().getActiveProfiles();
if (profiles != null && activeProfiles.length == 0) {
ctx.getEnvironment().setActiveProfiles(profiles);
ctx.refresh();
}
}
}
Using this approach has the added advantage of being able to set the active spring profile using a classpath properties file (this can differ depending on my active Maven profile). I also like this approach because I can use it for both web application and command line applications.

Related

WELD SE with JUnit 5 require enabling bean discovery programmatically

I'm setting up an example project for testing purposes which uses Weld SE and JUnit5 and for some reason, in my test classes, after initializing weld I observ that, for some reason, it's bean discovery is disabled, which in the end, it lead me to this error:
org.jboss.weld.exceptions.DeploymentException: WELD-001408: Unsatisfied dependencies for type Person with qualifiers #Default;
This is my test class :
#EnableWeld
public class SimpleTestA {
#Inject
Person p;
#Test
public void testThatItWorks() {
System.out.println("Hey");
}
}
located in :
projectName\core\model\src\test\java\com\aCompany\projectName\core\model\testmodel\SimpleTestA.java
This is my beans.xml :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-
instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd" version="1.1" bean-discovery-mode="all" />
located in :
projectName\core\model\src\main\resources\META-INF\beans.xml
The project structure is fairly simple, I just have a main module named "projectName" which is the parent of the sub-module named "core" which contains all that i pasted earlier. My dependencies list is this :
<dependencies>
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit-jupiter.aggregator.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.weld.se</groupId>
<artifactId>weld-se-core</artifactId>
<version>${weld.se.core.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.jboss.weld/weld-junit5 -->
<dependency>
<groupId>org.jboss.weld</groupId>
<artifactId>weld-junit5</artifactId>
<version>${weld.junit5.version}</version>
<scope>test</scope>
</dependency>
and those are my properties :
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<weld.se.core.version>3.0.1.Final</weld.se.core.version>
<weld.junit5.version>1.3.1.Final</weld.junit5.version>
<junit-jupiter.aggregator.version>5.4.0</junit-jupiter.aggregator.version>
If I modify the test adding the weld initialization attribute with explicit bean discovery activation, everything works very well:
#WeldSetup
WeldInitiator weldInitiator = WeldInitiator.of(WeldInitiator.createWeld().enableDiscovery());
What I'm missing ? If the beans.xml is present, shouldn't the bean discovery activated automatically? Thank you in advance.
So the thing here is that you are using Weld SE (well, weld-junit is), not Weld EE which you might know from servers such as WildFly.
In SE, the discovery is by default off and so called synthetic archive is used.
Synthetic archive only contains whatever you yourself feed it - classes as beans, packages to scan through etc. It doesn't scan whole classpath.
So to make your example work, you can either have the discovery on, or you can add the classes and packages you need via Weld.addPackages(), Weld.addClasses() and so on.
In context of Weld-junit this translates into WeldInitiator.createWeld().addPackages().
The reason why Weld SE (and weld-junit) doesn't perform the whole discovery is because you would effectively scan whole classpath including JDK packages and all. That takes time and on top of that you also discover tons of beans you don't need. Or you can pick up interceptors/alternatives that you didn't mean to. Last but not least, these are meant to be unit tests, so minimal deployments that test your beans.

Java Spring - how to inject #Value to a data object?

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.

No mapping found for HTTP request with URI [/WEB-INF/pages/MainPage.jsp] in DispatcherServlet with name 'dispatcherServlet' [duplicate]

This question already has answers here:
Why does Spring MVC respond with a 404 and report "No mapping found for HTTP request with URI [...] in DispatcherServlet"?
(13 answers)
Closed 6 years ago.
I am trying to configure Spring Boot using annotations.
I have class
#EnableWebMvc
#Configuration
#ComponentScan({
...
})
#EnableTransactionManagement
#EnableAutoConfiguration
#Import({ SecurityConfig.class })
public class AppConfig extends SpringBootServletInitializer {...}
which contains this View resolver which works fine.
#Bean
public InternalResourceViewResolver internalViewResolver() {
InternalResourceViewResolver viewResolver
= new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/pages/");
viewResolver.setSuffix(".jsp");
viewResolver.setOrder(1);
return viewResolver;
}
But after receiving name of JSP file application raise this error:
No mapping found for HTTP request with URI [/WEB-INF/pages/MainPage.jsp] in DispatcherServlet with name 'dispatcherServlet'.
I found solution for XML configuration:
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
But I am using annotation configuration, so this soultion is not suitable for me.
I tried to resolve this problem extending AbstractAnnotationConfigDispatcherServletInitializer
public class SpringMvcInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { AppConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
// I thought this method will be equivalent to XML config solution described above
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
But nothing has changed after this. By the way I looked at few examples with AbstractAnnotationConfigDispatcherServletInitializer, but I still don't understand how application can use this class when it's not annotated and no instances of this class are created. It's just declared and that's all. Maybe I need to create an instance of this class and attach it anywhere?
Anyway I see in log this line: Mapping servlet: 'dispatcherServlet' to [/]
So it looks like I have right servlet configuration.
I tried this solution but it doesn't help. I removed InternalResourceViewResolver and created application.properties with such content:
spring.view.prefix: /WEB-INF/jsp/
spring.view.suffix: .jsp
But after that I received: javax.servlet.ServletException: Could not resolve view with name 'MainPage' in servlet with name 'dispatcherServlet'
So what is the proper way to resolve this problem?
UPDATE
I tried to create a new simple project from scratch.
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>edu.springtest</groupId>
<artifactId>SpringTest</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.0.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
Main.java:
#Controller
#Configuration
#ComponentScan
#EnableAutoConfiguration
#SpringBootApplication
public class Main {
#RequestMapping("/")
String home() {
return "hello";
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Main.class, args);
}
}
application.properties:
spring.view.prefix: /WEB-INF/jsp/
spring.view.suffix: .jsp
Project's structure:
I run project with command mvn spring-boot:run and receive
edu.test.Main: Started Main in 2.365 seconds (JVM running for 5.476) in output.
But when I'm opening localhost:8080 I receive:
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Sat Dec 20 11:25:29 EET 2014
There was an unexpected error (type=Not Found, status=404).
No message available
But now I'm not receiving "No mapping found..." error in output. When I am opening localhost:8080 nothing is printed to output at all.
So what am I doing wrong?
Getting rid of your custom view resolver and setting the application properties was the right start. The whole point of Spring Boot is that it does a pretty good job of wiring these things up for you! :)
You should have a controller with the request mappings. Something like:
#RequestMapping("/")
public String mainPage() {
return "MainPage";
}
... which would use your MainPage.jsp template for any requests to /.
However, it's worth noting that by default, the contents of src/main/webapp don't get built into the executable application jar. To deal with his there are a couple of options I know of.
Option 1 - Move everything from /src/main/webapp/ to src/main/resources/static. I think this works for JSPs too. The only trouble here is that you can hot-replace code unless you're running the application in an IDE.
Option 2 - The alternative (which I tend to use) is to set up my Maven build to copy the contents of src/main/webapp into the classpath static folder when I build.
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<id>copy-resources</id>
<phase>validate</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${basedir}/target/classes/static</outputDirectory>
<resources>
<resource>
<directory>src/main/webapp</directory>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
For further reading (although this looks quite a bit like what you're already doing), there's a sample project, showing a Spring Boot app using JSP for templating:
https://github.com/spring-projects/spring-boot/blob/master/spring-boot-samples/spring-boot-sample-web-jsp/
I have the same problem, however I have not fix it yet, but more information can be provided for your investigation
With tracing in the source code of CachedResource in tomcat-embed-core.jar, I found if you run the application from development environment, for example Eclipse, the resource path will points to the project directory likes the following:
"C:/MyProject/MyApplication/src/main/webapp/WEB-INF/views/jsp/index.jsp"
where must contains this resource, but when I run it from the server with command of "java -jar ...", the resource path will be set to temp folder likes:
"/tmp/tomcat-docbase.3976292633605332325.8080/WEB-INF/views/jsp/index.jsp"
but /tmp/tomcat-docbase.3976292633605332325.8080 is empty. However I have no idea how to setup the resource path, but both of above shows the InternalResourceViewResolver will only deal with resources from a physical directory, not from classpath. It doesn't matter how you setup ResourceHandler.
I have this
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
registry.addResourceHandler("/WEB-INF/**").addResourceLocations("classpath:/WEB-INF/");
}
And packaged all resources to the package, the static resource is find, but jsp resource keeps saying 404.
For me, the solution was in Intellij IDEA.
In my tomcat server configuration, under the Deployment tab, I was missing the expected Application context entry. E.g. /myroot
Hope that helps someone!
Have you check that MainPage.jsp is present in WEB-INF/pages/ folder.
change public class AppConfig extends WebMvcConfigurerAdapter
Change Bean as :
#Bean
public ViewResolver getInternalResourceViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/pages/");
resolver.setSuffix(".jsp");
return resolver;
}
May this will help you

Specifying a Maven profile when running JUnit tests within a Spring webapp

Background
/pom.xml
...
<properties>
...
<jdbc.driver>com.mysql.jdbc.Driver</jdbc.driver>
<jdbc.url>jdbc:mysql://${database.host}/${database.name}</jdbc.url>
<jdbc.user>${database.user}</jdbc.user>
<jdbc.password>${database.password}</jdbc.password>
...
</properties>
...
<profiles>
<profile>
<id>dev</id>
<properties>
...
<database.name>database</database.name>
<database.host>localhost</database.host>
<database.user>root</database.user>
<database.password></database.password>
...
</properties>
</profile>
</profiles>
...
/src/main/resources/database.properties
...
jdbc.driver=${jdbc.driver}
jdbc.url=${jdbc.url}
jdbc.user=${jdbc.user}
jdbc.password=${jdbc.password}
...
/src/main/resources/spring/applicationContext.xml
<beans ... xmlns:p="http://www.springframework.org/schema/p" ...>
...
<bean
id="dataSource"
...
p:driverClassName="${jdbc.driver}"
p:url="${jdbc.url}"
p:username="${jdbc.user}"
p:password="${jdbc.password}"
... />
...
</beans>
/src/test/java/com/company/project/service/MyItemServiceImplTest.java
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "/spring/applicationContext.xml" })
public class MyItemServiceImplTest {
#Resource
private MyItemService myItemService;
#Test
public void testSave() {
MyItem myItem = new MyItem();
myItemService.save(myItem);
...
}
}
Question
When running the tests, it throws an exception:
java.lang.IllegalStateException: Failed to load ApplicationContext
...
Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name 'dataSource' defined in class path resource [spring/applicationContext.xml]: Could not resolve placeholder 'database.password'
...
I guess it's because I need to run the tests while specifying the dev profile like I do when I launch the webapp (using -P dev). But I can't make it work. Is it even possible?
PS
The filtered applicationContext.xml file (i.e. the one in /target/classes/spring/applicationContext.xml) is identical to the one in /src/*, but the filtered database.properties file (i.e. /target/classes/database.properties) looks like
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://${database.host}/${database.name}
jdbc.user=${database.user}
jdbc.password=${database.password}
It means that from the pom.xml file to the .properties one, the properties have been well filtered, but within the pom.xml itself, the properties that depend on the chosen profile won't get filtered. Probably because I want to specify anywhere the profile I need when launching the tests. But as I said before, -P dev doesn't seem work with JUnit...
Resource filtering is performed in the process-resources phase. So if you state mvn test -Pdev you will have passed that phase and all filtering has been done. It doesn't matter to JUnit what profile you are running since you are not doing anything else differently in that dev profile.

Spring DM and Services registry

I'm learning Spring DM and I have a problem with Service registry. I'm using ServiceMix 4.3.0 with embedded Felix instance. I have 2 bundles in my project.
The first one contains interface, and mock implementation. I want to publish them to OSGi service registry:
public interface PersonAPI {
public void listAll();
public List<PersonEntity> getAll();
public void addPerson(PersonEntity pe);
public PersonEntity getPerson(int id);
}
PersonEntity is a simple class with data, nothing special.
Mock implementation is contains just a list of PeopleEntity objects, so there is nothing interesting either.
Here is part of Spring configuration XML:
<bean id="personImpl" class="com.osgi.Person.impl.mock.PersonImpl" />
<osgi:service id="personLogic" ref="personImpl" interface="com.osgi.Person.PersonAPI" />
And part taken from pom.xml file:
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>2.3.4</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-Category>sample</Bundle-Category>
<Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
<Export-package>com.osgi.*</Export-package>
</instructions>
</configuration>
</plugin>
This installs fine on ServiceMix. Now I defined another bundle, here are most important parts:
public class PersonTester {
PersonAPI api;
public void init() {
System.out.println("PostConstruct:");
System.out.println("Have API " + api + " class " + api.getClass().getCanonicalName());
api.listAll(); //This line (or any API call, blows everything)
}
public PersonAPI getApi() {
return api;
}
public void setApi(PersonAPI api) {
this.api = api;
}
}
Spring configuration:
<osgi:reference id="personLogic" interface="com.osgi.Person.PersonAPI" />
<bean id="personTester" init-method="init" class="com.osgi.Person.PersonTester">
<property name="api" ref="personLogic" />
</bean>
Most important parts from pom.xml:
<dependencies>
<dependency>
<groupId>com.osgi</groupId>
<artifactId>Pserson-API</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
....
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>2.3.4</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-Category>sample</Bundle-Category>
<Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
<Import-package>com.Person.entity,com.osgi.Person</Import-package>
</instructions>
</configuration>
</plugin>
The good news is, that "behind" injected Spring proxy is my implementation class. I can see that when using api.toString(). However when I call any method defined in my proxy, I get an exception:
Exception in thread "SpringOsgiExtenderThread-88" org.springframework.beans.factory.BeanCreationException: Error creating bean
with name 'personTester': Invocation of init method failed; nested exception is org.springframework.aop.AopInvocationException: AOP configuration seems to be invalid: tried calling method [public abstract void com.osgi.Person.PersonAPI.listAll()] on target [PersonImpl [set=[]]]; nested exception is java.lang.IllegalArgumentException:
object is not an instance of declaring class
It looks like It looks like AOP is missing the target, but why? And how to fix this?
The problem is fixed. It was something in the environment.
I started building new project (with different artifact Id) on top of this tutorial and everything looks OK. Perhaps something cached somewhere (strange, because I flushed ServiceMix bundle cache and local maven repo). I try to reproduce it on different machine, and maybe I'll come up with some better explanation.

Categories