How to test the destruction of session scoped beans in jUnit? - spring

I have a session scoped bean in which I hold some user data and want to write a unit test to ensure that it remains session scoped.
I want to mock the starting and the ending of a session in jUnit and compare the values of the session scoped bean.
For now I have the following (rough drafts) of the unit test:
I use a custom context loader to register the session scope.
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.test.context.support.GenericXmlContextLoader;
import org.springframework.web.context.request.SessionScope;
public class SessionScopedGenericXmlContextLoader extends GenericXmlContextLoader {
#Override
protected void customizeBeanFactory(final DefaultListableBeanFactory beanFactory) {
beanFactory.registerScope("session", new SessionScope());
super.customizeBeanFactory(beanFactory);
}
}
The unit test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader = SessionScopedGenericXmlContextLoader.class,
locations = { "classpath:/test-applicationContext.xml","classpath:/cache-config.xml" })
public class CachingTest extends AbstractJUnit4SpringContextTests {
// Session scoping
protected MockHttpSession session;
protected MockHttpServletRequest request;
ApplicationContext ctx;
CacheManager cacheManager;
Cache accountSettingsCache;
#Autowired
#Qualifier("cachedMethods")
DummyCacheMethods methods;
#Autowired
private SecurityContextHolder contextHolder;
protected void startRequest() {
request = new MockHttpServletRequest();
request.setSession(session);
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
}
protected void endRequest() {
((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).requestCompleted();
RequestContextHolder.resetRequestAttributes();
request = null;
}
protected void startSession() {
session = new MockHttpSession();
}
protected void endSession() {
session.clearAttributes();
session = null;
}
#Before
public void constructSession() {
ctx = applicationContext;
cacheManager = (CacheManager) ctx.getBean("cacheManager");
accountSettingsCache = cacheManager.getCache("accountSettingsCache");
startRequest();
startSession();
}
#After
public void sessionClean() {
endRequest();
endSession();
contextHolder.clearContext();
}
#Test
// #DirtiesContext
public void checkSession1() {
final Authentication authentication = new Authentication() {
public String getName() {
return "Johny";
}
public void setAuthenticated(final boolean isAuthenticated) throws IllegalArgumentException {
}
public boolean isAuthenticated() {
return true;
}
public Object getPrincipal() {
final CustomUserDetails pr = new CustomUserDetails();
pr.setUsername("Johny");
return pr;
}
public Object getDetails() {
return null;
}
public Object getCredentials() {
return null;
}
public Collection<GrantedAuthority> getAuthorities() {
return null;
}
};
contextHolder.getContext().setAuthentication(authentication);
assertTrue(methods.getCurrentUserName().equals(accountSettingsCache.get("currentUser").get()));
assertTrue(methods.getCurrentUserName().equals(((CustomUserDetails) authentication.getPrincipal()).getUsername()));
}
#Test
// #DirtiesContext
public void testSession2() {
final Authentication authentication2 = new Authentication() {
public String getName() {
return "James";
}
public void setAuthenticated(final boolean isAuthenticated) throws IllegalArgumentException {
}
public boolean isAuthenticated() {
return true;
}
public Object getPrincipal() {
final CustomUserDetails pr = new CustomUserDetails();
pr.setUsername("James");
return pr;
}
public Object getDetails() {
return null;
}
public Object getCredentials() {
return null;
}
public Collection<GrantedAuthority> getAuthorities() {
return null;
}
};
SecurityContextHolder.setContext(contextHolder.getContext());
SecurityContextHolder.clearContext();
SecurityContextHolder.getContext().setAuthentication(authentication2);
assertTrue(methods.getCurrentUserName().equals(accountSettingsCache.get("currentUser").get()));
assertTrue(methods.getCurrentUserName().equals(((CustomUserDetails) authentication2.getPrincipal()).getUsername()));
}
#Test
public void testWiring() {
assertTrue("cacheManager is null", cacheManager != null);
assertTrue("accountSettingsCache is null", accountSettingsCache != null);
}
}
And the relevant function from DummyCachedMethods is:
#Cacheable(value = { "accountSettingsCache" }, key = "new String(\"currentUser\")")
public String getCurrentUserName() {
return WebUtils.getCurrentUser().getUsername();
}
WebUtils.getCurrentUser() just returns the current Principal from the SecurityContext.
But this does not work, the test does not pass at this line:
assertTrue(methods.getCurrentUserName().equals(((CustomUserDetails) authentication2.getPrincipal()).getUsername()));
Since the method call is cached, it still returns Johnny instead of James.
My cache related beans are:
<bean id="accountSettingsCache" class="org.springframework.cache.concurrent.ConcurrentMapCache" scope="session">
<constructor-arg>
<value>accountSettingsCache</value>
</constructor-arg>
<aop:scoped-proxy proxy-target-class="false" />
</bean>
<bean id="defaultCache" class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="defaultCache" />
<bean id="cacheManager" class="my.package.DynamicCacheManager" scope="session">
<property name="caches">
<set>
<ref bean="accountSettingsCache" />
<ref bean="defaultCache" />
</set>
</property>
<aop:scoped-proxy />
</bean>
accountSettingsCache is session scoped, I want to start a new session and destroy it.
It does pass if I uncomment the DirtiesContext annotations, but I guess it's not what I want.
What am I missing ?

Related

configured as a single instance, but instantiated twice

spring.version=5.2.7.RELEASE
public class Service15 {
public Service15(){
System.out.println("service15 init");
}
}
public class Service15Factory implements FactoryBean, BeanFactoryAware {
private BeanFactory beanFactory;
public Service15Factory(){
System.out.println("serviceFactory15 init");
}
#Override
public Object getObject() throws Exception {
beanFactory.getBean("service16");
return new Service15();
}
#Override
public Class<?> getObjectType() {
return Service15.class;
}
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}
public class Service16 {
private Service15 service15;
public Service16(){
System.out.println("service16 init");
}
public void setService15(Service15 service15){
this.service15 = service15;
}
}
configuration:
<bean id="service15" class="com.myspring.service.Service15Factory" scope="singleton">
</bean>
<bean id="service16" class="com.myspring.service.Service16">
<property name="service15" ref="service15"></property>
</bean>
I created three classes,service15 will be created by getObject of Service15Factory, and Service16 dependent on service15,service15 will be Initialized twice

How to inject bean in a Tapestry service

I want to inject bean in a Tapestry service (not in a page).
For the moment, I use this :
public class EntityRealm extends AuthorizingRealm {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/application-context-security.xml");
SecurityServices securityServices = (SecurityServices)ctx.getBean("securityServices");
It works, but I want use this :
public class EntityRealm extends AuthorizingRealm {
#Inject
private SecurityServices securityServices;
And my applicationContext is in the web.xml.
In this second case, the injection doesn't work. Why ?
AppModule.java :
public class AppModule
{
//#Resource(name = "realm")
#Inject
private static EntityRealm realm;
#Contribute(WebSecurityManager.class)
public static void addRealms(Configuration<EntityRealm> configuration) {
//EntityRealm realm = new EntityRealm();
configuration.add(realm);
}
public static void contributeFactoryDefaults( MappedConfiguration<String, Object> configuration)
{
configuration.override(SecuritySymbols.LOGIN_URL, "/login");
configuration.override(SecuritySymbols.UNAUTHORIZED_URL, "/login");
configuration.override(SecuritySymbols.SUCCESS_URL, "/index");
configuration.override(SymbolConstants.APPLICATION_VERSION, "2.0-SNAPSHOT");
}
public static void contributeApplicationDefaults(MappedConfiguration<String, Object> configuration)
{
configuration.add(SymbolConstants.HMAC_PASSPHRASE, new BigInteger(130, new SecureRandom()).toString(32));
configuration.add(SymbolConstants.SUPPORTED_LOCALES, "en,fr");
configuration.add( "tapestry.default-cookie-max-age", "31536000" );
}
public RequestFilter buildTimingFilter(final Logger log)
{
return new RequestFilter()
{
public boolean service(Request request, Response response, RequestHandler handler)
throws IOException
{
long startTime = System.currentTimeMillis();
try
{
return handler.service(request, response);
} finally
{
long elapsed = System.currentTimeMillis() - startTime;
log.info(String.format("Request time: %d ms", elapsed));
}
}
};
}
public void contributeRequestHandler(OrderedConfiguration<RequestFilter> configuration,
#Local
RequestFilter filter)
{
configuration.add("Timing", filter);
}
}
And the EntityRealm.java :
public class EntityRealm extends AuthorizingRealm {
//***************************************
//************* Attributes *************
//***************************************
//ApplicationContext ctx = new ClassPathXmlApplicationContext("/application-context-security.xml");
//SecurityServices securityServices = (SecurityServices)ctx.getBean("securityServices");
//#Resource(name = "securityServices")
#Inject
private SecurityServices securityServices;
//***************************************
//************ Constructors *************
//***************************************
public EntityRealm() {
super(new MemoryConstrainedCacheManager());
setName("myapprealm");
setAuthenticationTokenClass(UsernamePasswordToken.class);
}
//***************************************
//********** Public Methods *************
//***************************************
#Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
if (principals == null) throw new AuthorizationException("PrincipalCollection was null, which should not happen");
application-context.xml :
<bean id="realm" class="net.atos.m2m.telecom.ihm.services.EntityRealm">
<property name="securityServices" ref="securityServices"></property>
</bean>
<bean id="securityServices" class="net.atos.m2m.telecom.ihm.applicatif.services.security.impl.SecurityServicesImpl">
<property name="servicesTelSecu" ref="servicesTelSecu"></property>
<property name="converterSecDSPtoDTO" ref="converterSecDSPtoDTO"></property>
<property name="converterSecDTOtoDSP" ref="converterSecDTOtoDSP"></property>
</bean>
Can you help me ?
Thank you.
How i say in previous comment, if you create EntityRealm in this way .. new EntityRealm() the inject\autowire does not work.
You must define EntityRealm as bean .. XML or Annotation.
<bean id="entityRealm" class="package.EntityRealm"/>
<bean id="securityServices" class="package.SecurityServices"/>
You can use #Resource instead,
#Resource(name = "securityServices")
private SecurityServices securityServices;
And make sure that application-context-security.xml file is loaded by Spring.

Spring autowired bean is null NULL

Why my beans is null?
[b]servlet-context.xml [/b]
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"
>
<!-- <context:annotation-config/>-->
<context:component-scan base-package="by"/>
<context:property-placeholder location="classpath:database.properties"/>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/javakava"/>
<property name="username" value="root"/>
<property name="password" value="admin"/>
</bean>
</beans>
[b]controller[/b]
public class Controller extends HttpServlet {
private static final long serialVersionUID = 1L;
#Autowired
private CommandFactory commandFactory;
#Override
public void init(ServletConfig servletConfig) throws ServletException {
super.init(servletConfig);
}
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
performAction(request, response);
}
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
performAction(request, response);
}
private void performAction(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
String page = null;
String paramPage = request.getParameter(Constants.PARAM_PAGE);
try {
if (paramPage != null && !paramPage.isEmpty()) {
Command command = commandFactory.getCommand(paramPage);
page = command.execute(request);
// Commands c = Commands.valueOf(paramPage);
// Command command = c.getCommandClass().newInstance();
page = command.execute(request);
RequestDispatcher requestDispatcher = request
.getRequestDispatcher(page);
requestDispatcher.forward(request, response);
} else {
throw new IllegalAccessError(
"Error with access to class from Controller.java");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
LoginCommand - Here is autowared TestService bean. In IDEA it's look's good. But in debug mode my testService is null..
#Component
public class LoginCommand implements Command {
#Autowired
TestService testService;
public String execute(HttpServletRequest request) {
DaoCheckUserImpl id = new DaoCheckUserImpl();
String pass = request.getParameter(Constants.PASS);
String login = request.getParameter(Constants.LOGIN);
id.checkUser();
String userN = id.getUserN();
String userP = id.getUserP();
String userRole = id.getUserRole();
int userId = id.getUserId();
if (userN.equals(login) & userP.equals(pass) & userRole.equals("admin")) {
/*
*
* Here testService is null[/b]
*
*/
List<Test> tests = testService.getAllTests();
request.setAttribute(Constants.TESTS, tests);
User user = new User();
user.setLogin(login);
request.getSession().setAttribute(Constants.USER, user);
return Constants.MAIN_ADMIN_PAGE;
} else {
}
return Constants.ERROR_LOGIN_PAGE;
}
}
}
TestService
#Service
public class TestService {
#Autowired
public DaoTestImpl daoTestImpl;
public List<Test> getAllTests() {
return daoTestImpl.getAllTests();
}
public Test selectTest(String idTest) {
return daoTestImpl.selectTest(idTest);
}
public void deleteTest(Test test) {
daoTestImpl.deleteTest(test);
}
[b]DaoTestImpl [/b]
Here I using JdbcDaoSupport , datasource injected with constructor.
#Component
public class DaoTestImpl extends JdbcDaoSupport implements DaoTest {
#Autowired
public DaoTestImpl(DataSource dataSource) {
setDataSource(dataSource);
}
...
public List<Test> getAllTests() throws DAOException {
return getJdbcTemplate().query(("SELECT *FROM tests"), rowMapper);
}
CommandFactory
#Component
public class CommandFactory {
#Autowired
public LoginCommand loginCommand;
public Command getCommand(String paramPage) {
Commands command = Commands.valueOf(paramPage.toUpperCase());
switch (command) {
case LOGIN_COMMAND:
return loginCommand;
commands
public enum Commands { LOGIN_COMMAND
/*login_Command(LoginCommand.class),
How do you create LoginCommand object?
Autowired is used by Spring to inject the correct bean. So it works only if LoginCommand is created by Spring. If you performed a NEW, or if you use another framework without a proper integration with Spring, this can explain your issue (for example Jersey 2 without the proper configuration).
EDIT:
By the way, you can had "#Required" annotation. This will not fix your problem, but the new error message can help you to understqnd what happen (in particular it will help to see if LoginCommand object is really created by Spring and if the autowired failed [as I think] because the instance of TestService was NOT found [package naming issue, classloader issue, etc.])
Did you check if all your components are in the "by" package (that is specified in component-scan)?

Avoid recursive AOP

I have an Aspect that intercepts a method a() decorated with the annotation #Foo. This method
calls another method b() also decorated with the annotation #Foo. I want my aspect to intercept
only a() and not b(). How can I do this?
I have tried within() but with no success. With ThreadLocal it works but I am wondering
if there is a Spring solution.
#Component
#Order(value = 2)
#Aspect
public class FooAspect {
#Around(value = "#annotation(Foo)")
public Object aroundAdvice(ProceedingJoinPoint pjp, Foo foo) {
...
}
}
I don't think there is a way to do that with Aspects only. The interception will happen no matter what.
However, one solution is to use a ThreadLocal Byte to flag that you've already applied the advice.
private ThreadLocal<Byte> flag = new ThreadLocal<>();
#Around(value = "#annotation(Foo)")
public Object aroundAdvice(ProceedingJoinPoint pjp, Foo foo) throws Throwable {
if (flag.get() == null) {
try {
flag.set((byte) 1); // or 0, whatever
// apply advice
return pjp.proceed();
} finally {
flag.remove();
}
} else {
// don't apply advice
return pjp.proceed();
}
}
You'll need to catch or throws the Throwable thrown from proceed().
There is a cflow like poincut in the framework, but it's not supported by Aspectj-style.
However you can use it with aop-schema configuration approach.
For example:
public class NotInFlowPoincut extends AspectJExpressionPointcut {
private ControlFlowPointcut cflow;
private Class<?> flowClass;
private String methodName;
public NotInFlowPoincut() {
}
#PostConstruct
public void init() {
cflow = new ControlFlowPointcut(flowClass, methodName);
}
#Override
public boolean matches(Method method, Class targetClass, Object[] args) {
return super.matches(method, targetClass, args) && ! cflow.matches(method, targetClass, args);
}
public Class<?> getFlowClass() {
return flowClass;
}
public void setFlowClass(Class<?> flowClass) {
this.flowClass = flowClass;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
}
public class FooAspect {
public Object aroundAdvice(ProceedingJoinPoint pjp, Foo foo) throws Throwable {
System.out.println("in aroundAdvice");
return pjp.proceed();
}
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/context.xml");
A a = ctx.getBean(A.class);
a.a();
B b = ctx.getBean(B.class);
b.b();
}
}
public class A {
#Autowired B b;
#Foo
public void a() {
System.out.println("In a()");
b.b();
}
}
public class B {
#Foo
public void b() {
System.out.println("In b()");
}
}
And finally
<bean id="cflow" class="test.NotInFlowPoincut">
<property name="expression" value="#annotation(foo) " />
<property name="methodName" value="aroundAdvice" />
<property name="flowClass" value="test.FooAspect" />
</bean>
<bean id="foo" class="test.FooAspect" />
<bean id="a" class="test.A" />
<bean id="b" class="test.B" />
<aop:config proxy-target-class="true">
<aop:aspect ref="foo" >
<aop:around method="aroundAdvice" pointcut-ref="cflow" />
</aop:aspect>
</aop:config>
Will output:
in aroundAdvice
In a()
In b()
in aroundAdvice
In b()
That seems to be what you are looking for.
Note that cflow poincut is slow, see javadoc.

Create a spring like wiring class

I want to create a bean that will act as a provider.
I will give it the class that it should return and the list of properties that I should set before returning it.
so basically it looks like this:
<bean id="somethingFactory" class="foo.bar.SomethingFactory">
<property name="implClass" value="foo.bar.SomehtingImpl" />
<property name="properties">
<props>
<prop key="prop1">prop1Value</prop>
<prop key="prop2">prop2Value</prop>
</props>
</property>
</bean>
The "SomethingFactory" has a provide() method that will return an instance of "SomehtingImpl".
The question is how can I use Spring to do it?
Make SomethingFactory a FactoryBean, extend AbstractFactoryBean and use a BeanWrapper to populate the properties from the input parameters.
Here's a sample implementation:
public class ServiceFactoryBean<T> extends AbstractFactoryBean<T> {
private Class<T> serviceType;
private Class<? extends T> implementationClass;
private Map<String, Object> beanProperties;
#Override
public void afterPropertiesSet() {
if (serviceType == null || implementationClass == null
|| !serviceType.isAssignableFrom(implementationClass)) {
throw new IllegalStateException();
}
}
#Override
public Class<?> getObjectType() {
return serviceType;
}
public void setBeanProperties(final Map<String, Object> beanProperties) {
this.beanProperties = beanProperties;
}
public void setImplementationClass(
final Class<? extends T> implementationClass) {
this.implementationClass = implementationClass;
}
public void setServiceType(final Class<T> serviceType) {
this.serviceType = serviceType;
}
#Override
protected T createInstance() throws Exception {
final T instance = implementationClass.newInstance();
if (beanProperties != null && !beanProperties.isEmpty()) {
final BeanWrapper wrapper = new BeanWrapperImpl(instance);
wrapper.setPropertyValues(beanProperties);
}
return instance;
}
}

Resources