Intergrating AEM/CQ5 and Spring Framework? - spring

I have been trying to integrate Spring framework within AEM6/CQ5.
I am following this tutorial. LINK
As per the tutorial I have installed the NEBA package.
All NEBA bundles are active in OSGi console.
Then I created my own Maven CQ5 project, I added the dependencies of Neba annotations and Spring. My project was also successfully deployed in CQ5(bundle is active).
I tried to use a ResourceModel annotation of NEBA. But this model does not appear in the model registry of NEBA.
I mapped the ResourceModel to a content component that i created "linkComponent".
When the user drags and drops this on any parsys the resource node has the properties linkName and linkURL.
I tried accessing these values in JSP but I failed.
Please See code below :
package com.zensar.neba;
import org.apache.sling.api.resource.Resource;
import io.neba.api.annotations.Path;
import io.neba.api.annotations.ResourceModel;
#ResourceModel(types = "zensar-neba/components/content/linkComponent")
public class LinkComponent {
private String linkName;
private String linkURL;
public String getLinkName() {
return linkName;
}
public void setLinkName(String linkName) {
this.linkName = linkName;
}
public String getLinkURL() {
return linkURL;
}
public void setLinkURL(String linkURL) {
this.linkURL = linkURL;
}
}
Please See JSP Code of linkComponent below:
<%#include file="/libs/foundation/global.jsp"%>
<%#taglib prefix="neba" uri="http://neba.io/1.0"%>
<neba:defineObjects />
Link Component
Click Here ${m.linkName}
Then I tried creating an Controller using Spring annotation but I got "Path not found" what am I missing.
Please see code below:
package com.zensar.neba;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
#Controller
public class DemoController {
#RequestMapping("/echo/{param}")
#ResponseBody
public String echo(#PathVariable("param") String paramToEcho) {
return "Hello "+ paramToEcho;
}
}
I called the controller link this : http://localhost:4502/bin/mvc.do/echo/Oliver
IMPORTANT NOTE:
All my bundles are active

If all bundles are up and running and you are seeing the model registry, it means everything is running smoothly. But: your model doesn't show up, this means that the application context of your bundle isn't running. NEBA uses gemini-blueprint, the reference implementation of the OSGi blueprint specification.
Do you have an xml file, e.g. blueprint.xml, in the OSGI-INF/blueprint folder of your bundle? It should look like this:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:bp="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.osgi.org/xmlns/blueprint/v1.0.0
http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<context:component-scan base-package="com.zensar.neba" />
</beans>
If you do, what does your error.log say when your bundle is starting?
For reference, here is a sample webapp using NEBA: https://github.com/unic/publication-neba-adaptto-2015
One more thing: NEBA uses private field injection, so you don't need to have any setters on your bean.

I'm glad to have helped! Just to answer your last question: In a maven project, the right location for the blueprint XML file would be in the src/main/resources/OSGI-INF/blueprint folder of the maven module containing your models / controllers.

Related

Reading sling:OsgiConfig into #ObjectClassDefinition

We have a OSGI Service (using R7 DS annotation). We are using OCD as innerclass within the service.
Also, we would like the component to read properties from the predefined sling:osgiconfig nodes in JCR.
Configuration policy is defined as required.
When the component/service load it goes into a "no config" state.
Need help to read these configs from sling:osgiCongig nodes.
The sling:OsgiConfig configurations should be defined with the Service implementation class name that's supposed to consume the OCD configurations, and not with the OCD name.
OCDs are basically interfaces that can have multiple implementations. Hence the sling:OsgiConfig nodes have to be defined with the service implementation class name. For example let's say you have a SampleServiceImpl.class defining a SampleOCDConfig.class as below:
#Component(service = SampleService.class)
#Designate(ocd = SampleServiceImpl.Config.class)
public class SampleServiceImpl implements SampleService {
public static final String DEFAULT_CUSTOM_CONFIG = "default configuration value";
#ObjectClassDefinition(name = "Sample OCD Configuration")
#interface Config {
#AttributeDefinition(name = "The custom config", defaultValue = DEFAULT_CUSTOM_CONFIG)
String custom_config() default DEFAULT_CUSTOM_CONFIG;
}
...
Now when you define the configurations for above setup, you must define it like below:
/apps/your_project/config/your.sample.service.package.path.SampleServiceImpl.xml
with following content:
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
jcr:primaryType="sling:OsgiConfig"
custom.config="default configuration value" />

Duplicate event received on listener when using ScopedProxyMode.TARGET_CLASS

In some cases, we need to write to database in a Spring -application within an ApplicationListener, so we need transactions within the listener using #Transactional-annotation. These listeners are extended from an abstract baseclass, so normal ScopedProxyMode.INTERFACES won't do, as Spring container complains about expecting a bean of the abstract class-type, not "[$Proxy123]". However, using Scope(proxyMode=ScopedProxyMode.TARGET_CLASS), the listener receives the same event twice. We are using Spring version 3.1.3.RELEASE. (Edit: Still occurring with version 3.2.4.RELEASE)
Digging into Spring source with debugger, I found out that org.springframework.context.event.AbstractApplicationEventMulticaster.getApplicationListeners returns a LinkedList that contains the same listener twice (same instance: [com.example.TestEventListenerImpl#3aa6d0a4, com.example.TestEventListenerImpl#3aa6d0a4]), if the listener is a ScopedProxyMode.TARGET_CLASS.
Now, I can work around this by placing the code handling database write into a separate class and putting the #Transactional there, but my question is, is this a bug in Spring or expected behavior? Are there any other workarounds so we wouldn't need to create separate service-classes (ie. handle the transaction in the listener, but don't get the same event twice) for even the simplest cases?
Below is a smallish example showing the problem.
With #Scope(proxyMode=ScopedProxyMode.TARGET_CLASS) in TestEventListenerImpl, the output is as follows:
Event com.example.TestEvent[source=Main] created by Main
Got event com.example.TestEvent[source=Main]
Got event com.example.TestEvent[source=Main]
With #Scope(proxyMode=ScopedProxyMode.TARGET_CLASS) removed from TestEventListenerImpl, the output is:
Event com.example.TestEvent[source=Main] created by Main
Got event com.example.TestEvent[source=Main]
So it seems that TARGET_CLASS -scoped beans get inserted twice into the listener list.
Example:
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.example/**"/>
</beans>
com.example.TestEvent
public class TestEvent extends ApplicationEvent
{
public TestEvent(Object source)
{
super(source);
System.out.println("Event " + this + " created by " + source);
}
}
com.example.TestEventListener
public interface TestEventListener extends ApplicationListener<TestEvent>
{
#Override
public void onApplicationEvent(TestEvent event);
}
com.example.TestEventListenerImpl
#Component
#Scope(proxyMode=ScopedProxyMode.TARGET_CLASS) //If commented out, the event won't be received twice
public class TestEventListenerImpl implements TestEventListener
{
#Override
public void onApplicationEvent(TestEvent event)
{
System.out.println("Got event " + event);
}
}
com.example.ListenerTest
public class ListenerTest
{
public static void main(String[] args)
{
ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
SimpleApplicationEventMulticaster eventMulticaster = appContext.getBean(SimpleApplicationEventMulticaster.class);
//This is also needed for the bug to reproduce
TestEventListener listener = appContext.getBean(TestEventListener.class);
eventMulticaster.multicastEvent(new TestEvent("Main"));
}
}
I can't speak to if this is a bug or expected behavior, but here's the dirty:
Declaring a bean like
#Component
#Scope(proxyMode=ScopedProxyMode.TARGET_CLASS) //If commented out, the event won't be received twice
public class TestEventListenerImpl implements TestEventListener
{
Creates two BeanDefinition instances:
A RootBeanDefinition describing the Scoped bean.
A ScannedGenericBeanDefinition describing the actual object.
The ApplicationContext will use these bean definitions to create two beans:
A ScopedProxyFactoryBean bean. This is a FactoryBean that wraps the TestEventListenerImpl object in a proxy.
A TestEventListenerImpl bean. The actual TestEventListenerImpl object.
Part of the initialization process is to register beans that implement the ApplicationListener interface. The TestEventListenerImpl bean is created eagerly (right away) and registered as an ApplicationListener.
The ScopedProxyFactoryBean is lazy, the bean (proxy) it's supposed to create is only generated when requested. When that happens, it also gets registered as an ApplicationListener. You only see this when you explicitly request it
TestEventListener listener = appContext.getBean(TestEventListener.class);
Or implicitly by using #Autowired to inject it into another bean. Note that the actual target object is added, not the proxy.

Managing spring beans creation and dictionary

I've to manage the association between bean instance and some type of resource. More specifically, when I receive some data associated to Resource A, I have to dispatch the data to the create an instance of some bean type every time I receive data associated with a specified resource. For example, if I receive some data associated with Resource "a", then i have to dispatch this data to a Bean instance associated with "a". If there isn't a Bean instance associated to Resource "a", then it has to be created. The number of Resource isn't statically known, and it will be known only during execution.
I understand I need some type of dictionary to store the association between Resource id and bean instance, building something like a Factory to manage bean creation.
How can I implement this in Spring? What's the proper way?
the Spring ApplicationContext can be referred to in this 'dictionary' fashion. that is, you specify to the application context to 'setup' the 'resource beans'. you then map the 'received data' via way of a Map object, that, in turn, holds a reference to the bean. once the data maps to the bean, you can then retrieve the bean and process the received data. here's a brief example
here's a resource service interface
package de.incompleteco.spring.service;
public interface ResourceService {
public void processData(Object data);
}
here's an implementation to process 'TypeA' data
package de.incompleteco.spring.service;
public class TypeAResourceService implements ResourceService {
public void processData(Object data) {
//do something for 'type A' data
}
}
here's a 'delegate' service
package de.incompleteco.spring.service;
import java.util.Map;
import de.incompleteco.spring.domain.TypeA;
import de.incompleteco.spring.domain.TypeB;
public class DelegateResourceService implements ResourceService {
private Map<String,ResourceService> services;
public void processData(Object data) {
if (data instanceof TypeA) {
services.get(TypeA.class.getSimpleName()).processData(data);
} else if (data instanceof TypeB) {
services.get(TypeA.class.getSimpleName()).processData(data);
} else {
throw new IllegalArgumentException("...");
}
}
public void setServices(Map<String, ResourceService> services) {
this.services = services;
}
}
the 'delegate' service is responsible for retrieving the right service from a map to process the right data (keyed by Class Name)
here's the spring configuration to support
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!-- type 'a' bean -->
<bean id="typeA" class="de.incompleteco.spring.service.TypeAResourceService"/>
<!-- type 'b' bean -->
<bean id="typeB" class="de.incompleteco.spring.service.TypeBResourceService"/>
<util:map id="dictionary">
<entry key="TypeA" value-ref="typeA"/>
<entry key="TypeB" value-ref="typeB"/>
</util:map>
<bean class="de.incompleteco.spring.service.DelegateResourceService">
<property name="services" ref="dictionary"/>
</bean>
</beans>
this configuration does the following;
sets up services for type A and B data
maps those services to keys
sets up the delegate service, setting the map to be used
this pattern is not the only way of doing it, but gives you the following;
ability to configure the service beans independantly
the delegate only needs a map to process
the constraint is that the service objects have to be of the same interface
you can think of the Spring ApplicationContext as a giant factory, but (in many ways) a whole lot simpler to setup.

Spring #Value not getting set

I'm sure this has been asked 1000 times, because i've seen them, however I'm missing something.
Context:
<beans profile="localDev">
<util:properties id="propertiesLocalDev"location="classpath:/localDev.properties"/>
</beans>
<beans profile="test">
<util:properties id="properties-test" location="classpath:/test.properties"/>
</beans>
Init:
System.setProperty("spring.profiles.active", "localDev");
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.load("classpath:applicationContext.xml");
ctx.refresh();
Config:
#Configuration
public class AppConfig {
#Value("${test.value}")
private String testValue;
...
Logs:
INFO: Loading properties file from class path resource [localDev.properties]
Properties:
test.value=ugh
So it seems the properties are getting read, however the the value in AppConfig.testValue is not getting set. I have tried pure java java/xml etc... Some configs break some work, tried using #PropertySource, but the constant is testValue never gets set, so I'm fundamentally doing something wrong.
The overall goal is to load different properties files depending on different profiles.
Can anyone see what I'm doing wrong?
Thanks
You will also a need a PropertySourcesPlaceholderConfigurer which can resolve the property for you. This is configured using:
<context:property-placeholder location="..."
local-override="true" properties-ref="propertiesLocalDev" />
With this your property value should resolve cleanly.
This also should work - using Spring-EL:
#Value("#{#propertiesLocalDev['test.value']}")
private String testValue;
Try
public class AppConfig {
#Autowired
private String testValue;
}
If the variable is properly autowired you can use
String sUgh = testValue.getProperty("test.value"); // = "ugh"
I alsou would use plain
<util:properties id="propertiesLocalDev"location="classpath:/localDev.properties"/>
instead of using the
<beans profile>
tag.
You have to use something like, in XML only use following
<util:properties id="test" location="classpath:fn-test-configuration.properties" />
Now, following way you can use the property values in class
#Value("#{test.test.value}")
private String testValue;
I have used the same way and it is working fine.

Spring + Mockito test injection

My question is very similar to the issue raised in Injecting Mockito mocks into a Spring bean. In fact, I believe the accepted answer there might actually work for me. However, I've got one issue with the answer, and then some further explanation in case the answer there is not in fact my answer.
So I followed the link in the aforementioned post to the Springockito website. I altered my test-config.xml to include something similar to the following:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mockito="http://www.mockito.org/spring/mockito"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.mockito.org/spring/mockito http://www.mockito.org/spring/mockito.xsd">
...
<mockito:mock id="accountService" class="org.kubek2k.account.DefaultAccountService" />
...
</beans>
There seems to be something wrong with the www.mockito.org redirect currently, so I found the XSD code at https://bitbucket.org/kubek2k/springockito/raw/16143b32095b/src/main/resources/spring/mockito.xsd and altered the final entry in xsi:schemaLocation to point to this bitbucket link.
Running mvn test then produced the following error (newlines added for readability):
Caused by: org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException:
Line 43 in XML document from class path resource [spring/test-context.xml] is invalid;
nested exception is org.xml.sax.SAXParseException; lineNumber: 43; columnNumber: 91;
The prefix "mockito" for element "mockito:mock" is not bound.
So the question regarding Springockito is: Is it possible anymore to include this? What am I missing?
Now, on to the further explanation...
I have an interface whose implementation I'm trying to test:
public interface MobileService {
public Login login(Login login);
public User getUser(String accessCode, Date birthDate);
}
The implementation contains a DAO that Spring #Autowires in for me:
#Service
public class MobileServiceImpl implements MobileService {
private MobileDao mobileDao;
#Autowired
public void setMobileDao(MobileDao mobileDao) {
this.mobileDao = mobileDao;
}
}
I don't want to alter my interface to include a setMobileDao method, because that would be adding code just to support my unit testing. I'm trying to mock out the DAO since the actual SUT here is the ServiceImpl. How can I achieve this?
You don't want to test your interface: it contains no code at all. You want to test your implementation. So the setter is available. Just use it:
#Test
public void testLogin() {
MobileServiceImpl toTest = new MobileServiceImpl();
toTest.setMobileDao(mockMobileDao);
// TODO call the login method and check that it works as expected.
}
No need for a spring context. Just instanciate your POJO service, inject mock dependencies manually, and test the methods you want to test.
After struggling with the Springockito XSD issue, for a while, I found a much simpler solution. Let Spring inject the mock for you using a factory method, i.e. in applicationContext.xml put:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.gerrydevstory.mycoolbank.AccountsDAO"/>
</bean>
<bean class="com.gerrydevstory.mycoolbank.BankingService"/>
</beans>
where the AccountsDAO bean is injected into the BankingService class. The corresponding JUnit test case is:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("/BankingServiceTest.xml")
public class BankingServiceTest {
#Autowired private BankingService bankingService;
#Autowired private AccountsDAO mockAccountsDAO;
#Test
public void testTransfer() throws Exception {
// Setup 2 accounts
Account acc1 = new Account();
acc1.setBalance(800.00);
Account acc2 = new Account();
acc2.setBalance(200.00);
// Tell mock DAO to return above accounts when 1011 or 2041 is queried respectively
when(mockAccountsDAO.findById(1011)).thenReturn(acc1);
when(mockAccountsDAO.findById(2041)).thenReturn(acc2);
// Invoke the method to test
bankingService.transfer(1011, 2041, 500.00);
// Verify the money has been transferred
assertEquals(300.00, acc1.getBalance(), 0.001);
assertEquals(700.00, acc2.getBalance(), 0.001);
}
}
Personally I find this very elegant and easy to understand. For more details, see the original blog post.
You have three options to set your mock dao:
Test the implementation - which gives a seam for your mock via the setDao method. (as JB's answer)
Add the setDao method to the interface - not desired since you don't want to add code just to support your tests.
Add a constructor to the impl class to accept the dao - not desired for same reason as #2.
If you wanted to do #3, you'll need to add a constructor to the MobileService that accepts the MobileDao.
public MobileServiceImpl(MobileDao mobileDao) {
this.mobileDao = mobileDao;
}
Then your test will look like this:
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.*;
import java.util.Date;
import org.junit.Before;
import org.junit.Test;
public class MobileServiceImplTest {
private MobileService systemUnderTest;
private MobileDao mobileDao;
#Before
public void setup() {
mobileDao = mock(MobileDao.class);
systemUnderTest = new MobileServiceImpl(mobileDao);
}
#Test
public void testGetUser() {
//if you need to, configure mock behavior here.
//i.e. when(mobileDao.someMethod(someObject)).thenReturn(someResponse);
systemUnderTest.getUser("accessCode", new Date());
verify(mobileDao).getUser("JeffAtwood");
}
}
Please note that you have not provided us with the details of the MobileDao so I created a getUser method that accepts a String.
To make the test pass, your MobileServiceImpl would just need this:
mobileDao.getUser("JeffAtwood");
The problem looks like your classpath doesn't contain actual springockito jar - You don't have to change the URL's - these are only the tokens that are used internally by spring - they are not being resolved - all You need is a new enough Spring distribution and springockito on classpath.
Kuba (creator of aforementioned lib :) )
I had same problem, I wanted use springockito by according their wiki but validations xml throw errors. So when i tried go to locations where xsd supposed to be, that hasn't. So i read this and get to working with this:
xmlns:mockito="http://www.mockito.org/spring/mockito"
xsi:schemaLocation="http://www.mockito.org/spring/mockito
https://bitbucket.org/kubek2k/springockito/raw/16143b32095b/src/main/resources/spring/mockito.xsd">
But when i looking at this link, had bad feeling. It seems to me that's not permanent stable link (you must check if i wrong)

Resources