/**
* This file Copyright (c) 2011-2012 Magnolia International
* Ltd. (http://www.magnolia-cms.com). All rights reserved.
*
*
* This file is dual-licensed under both the Magnolia
* Network Agreement and the GNU General Public License.
* You may elect to use one or the other of these licenses.
*
* This file is distributed in the hope that it will be
* useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
* Redistribution, except as permitted by whichever of the GPL
* or MNA you select, is prohibited.
*
* 1. For the GPL license (GPL), you can redistribute and/or
* modify this file under the terms of the GNU General
* Public License, Version 3, as published by the Free Software
* Foundation. You should have received a copy of the GNU
* General Public License, Version 3 along with this program;
* if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* 2. For the Magnolia Network Agreement (MNA), this file
* and the accompanying materials are made available under the
* terms of the MNA which accompanies this distribution, and
* is available at http://www.magnolia-cms.com/mna.html
*
* Any modifications to this file must keep this entire header
* intact.
*
*/
package info.magnolia.objectfactory.guice.lifecycle;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import info.magnolia.objectfactory.configuration.ComponentProviderConfiguration;
import info.magnolia.objectfactory.guice.GuiceComponentProvider;
import info.magnolia.objectfactory.guice.GuiceComponentProviderBuilder;
import info.magnolia.objectfactory.guice.lifecycle.packageprotected.LifecycleExtendsClassWithPackageProtectedMethods;
import info.magnolia.objectfactory.guice.lifecycle.packageprotected.LifecyclePackageProtectedMethod;
/**
* This test case makes sure that @PostConstruct and @PreDestroy is implemented according to JSR250 when used in a class
* hierarchy and on private and package protected methods.
*
* Support for @PreDestroy is currently disabled because it proved unreliable for lazy singletons and scoped objects.
*/
public class GuiceComponentProviderLifecycleTest {
private boolean shouldTestDestroy = false;
private List<String> events = new ArrayList<String>();
/**
* Tests the simplest case where a class has public annotated methods and no inheritance is involved.
*
* @see LifecycleSimple
*/
@Test
public void testSimpleLifeCycle() {
// GIVEN
GuiceComponentProvider p = createProvider(LifecycleSimple.class);
// WHEN
p.getComponent(LifecycleSimple.class);
// THEN
assertEvent("LifecycleSimple.init");
assertNoMoreEvents();
if (shouldTestDestroy) {
// WHEN
p.destroy();
// THEN
assertEvent("LifecycleSimple.destroy");
assertNoMoreEvents();
}
}
/**
* Tests support for having multiple annotated methods. Note that they can be invoked in any order since the order
* methods appear in the binary files are not guaranteed to match that in the source files.
*
* @see LifecycleMultiple
*/
@Test
public void testMultipleAnnotatedMethods() {
// GIVEN
GuiceComponentProvider p = createProvider(LifecycleMultiple.class);
// WHEN
p.getComponent(LifecycleMultiple.class);
// THEN
assertEventsInAnyOrder("LifecycleMultiple.init", "LifecycleMultiple.init2");
assertNoMoreEvents();
if (shouldTestDestroy) {
// WHEN
p.destroy();
// THEN
assertEventsInAnyOrder("LifecycleMultiple.destroy", "LifecycleMultiple.destroy2");
assertNoMoreEvents();
}
}
/**
* Tests that methods are invoked in the correct order when inheritance is used. Methods with @PostConstruct are
* invoked top down, and methods with @PreDestroy are invoked bottom up.
*
* @see LifecycleExtends
*/
@Test
public void testExtends() {
// GIVEN
GuiceComponentProvider p = createProvider(LifecycleExtends.class);
// WHEN
p.getComponent(LifecycleExtends.class);
// THEN
assertEvent("LifecycleSimple.init");
assertEvent("LifecycleExtends.init2");
assertNoMoreEvents();
if (shouldTestDestroy) {
// WHEN
p.destroy();
// THEN
assertEvent("LifecycleExtends.destroy2");
assertEvent("LifecycleSimple.destroy");
assertNoMoreEvents();
}
}
/**
* Tests that the implementation detects overridden methods and invokes them only once. Otherwise it would call
* it twice.
*
* @see LifecycleOverrides
*/
@Test
public void testOverrides() {
// GIVEN
GuiceComponentProvider p = createProvider(LifecycleOverrides.class);
// WHEN
p.getComponent(LifecycleOverrides.class);
// THEN
assertEvent("LifecycleOverrides.init");
assertNoMoreEvents();
if (shouldTestDestroy) {
// WHEN
p.destroy();
// THEN
assertEvent("LifecycleOverrides.destroy");
assertNoMoreEvents();
}
}
/**
* Tests that methods that override an annotated method in the super class and does not itself declare the
* annotation are not called.
*
* @see LifecycleOverridesRemovesAnnotations
*/
@Test
public void testOverridesRemovesAnnotations() {
// GIVEN
GuiceComponentProvider p = createProvider(LifecycleOverridesRemovesAnnotations.class);
// WHEN
p.getComponent(LifecycleOverridesRemovesAnnotations.class);
// THEN
assertNoMoreEvents();
if (shouldTestDestroy) {
// WHEN
p.destroy();
// THEN
assertNoMoreEvents();
}
}
/**
* Tests that private lifecycle methods are invoked.
*
* @see LifecyclePrivateMethods
*/
@Test
public void testPrivateMethods() {
// GIVEN
GuiceComponentProvider p = createProvider(LifecyclePrivateMethods.class);
// WHEN
p.getComponent(LifecyclePrivateMethods.class);
// THEN
assertEvent("LifecyclePrivateMethods.init");
assertNoMoreEvents();
if (shouldTestDestroy) {
// WHEN
p.destroy();
// THEN
assertEvent("LifecyclePrivateMethods.destroy");
assertNoMoreEvents();
}
}
/**
* Tests that private lifecycle methods in a class hierarchy where method signatures are identical in different
* classes are all called.
*
* @see LifecyclePrivateMethods
*/
@Test
public void testSameNamePrivateMethods() {
// GIVEN
GuiceComponentProvider p = createProvider(LifecycleSameNamePrivateMethods.class);
// WHEN
p.getComponent(LifecycleSameNamePrivateMethods.class);
// THEN
assertEvent("LifecyclePrivateMethods.init");
assertEvent("LifecycleSameNamePrivateMethods.init");
assertNoMoreEvents();
if (shouldTestDestroy) {
// WHEN
p.destroy();
// THEN
assertEvent("LifecycleSameNamePrivateMethods.destroy");
assertEvent("LifecyclePrivateMethods.destroy");
assertNoMoreEvents();
}
}
/**
* Tests that package protected methods are invoked.
*
* @see LifecyclePackageProtectedMethod
*/
@Test
public void testLifeCycleOnPackageProtectedMethods() {
// GIVEN
GuiceComponentProvider p = createProvider(LifecyclePackageProtectedMethod.class);
// WHEN
p.getComponent(LifecyclePackageProtectedMethod.class);
// THEN
assertEvent("LifecyclePackageProtectedMethod.init");
assertNoMoreEvents();
if (shouldTestDestroy) {
// WHEN
p.destroy();
// THEN
assertEvent("LifecyclePackageProtectedMethod.destroy");
assertNoMoreEvents();
}
}
/**
* Tests that in a case where a class A that inherits from another class B and has methods with same signature as
* package protected methods in that class, methods on both classes A and B are invoked because they were not
* overridden.
*
* @see LifecycleExtendsClassWithPackageProtectedMethodsInOtherPackage
*/
@Test
public void testLifeCycleOnClassThatExtendsClassWithPackageProtectedMethods() {
// GIVEN
GuiceComponentProvider p = createProvider(LifecycleExtendsClassWithPackageProtectedMethodsInOtherPackage.class);
// WHEN
p.getComponent(LifecycleExtendsClassWithPackageProtectedMethodsInOtherPackage.class);
// THEN
assertEvent("LifecyclePackageProtectedMethod.init");
assertEvent("LifecycleExtendsClassWithPackageProtectedMethodsInOtherPackage.init");
assertNoMoreEvents();
if (shouldTestDestroy) {
// WHEN
p.destroy();
// THEN
assertEvent("LifecycleExtendsClassWithPackageProtectedMethodsInOtherPackage.destroy");
assertEvent("LifecyclePackageProtectedMethod.destroy");
assertNoMoreEvents();
}
}
/**
* Tests that the implementation detects overridden package protected methods and invokes them only one. If it
* doesn't they would be invoked twice.
*
* @see LifecycleExtendsClassWithPackageProtectedMethods
*/
@Test
public void testLifeCycleOnClassThatOverridesPackageProtectedMethods() {
// GIVEN
GuiceComponentProvider p = createProvider(LifecycleExtendsClassWithPackageProtectedMethods.class);
// WHEN
p.getComponent(LifecycleExtendsClassWithPackageProtectedMethods.class);
// THEN
assertEvent("LifecycleExtendsClassWithPackageProtectedMethods.init");
assertNoMoreEvents();
if (shouldTestDestroy) {
// WHEN
p.destroy();
// THEN
assertEvent("LifecycleExtendsClassWithPackageProtectedMethods.destroy");
assertNoMoreEvents();
}
}
private GuiceComponentProvider createProvider(Class<?> clazz) {
ComponentProviderConfiguration configuration = new ComponentProviderConfiguration();
configuration.registerInstance(List.class, events);
configuration.registerImplementation(clazz);
return new GuiceComponentProviderBuilder().withConfiguration(configuration).build();
}
private void assertNoMoreEvents() {
if (!events.isEmpty()) {
fail("Unexpected events: " + events);
}
}
private void assertEvent(String expectedString) {
if (events.isEmpty()) {
fail("Expected event: " + expectedString + ", but the event list is empty");
}
String actualString = events.get(0);
assertEquals(expectedString, actualString);
events.remove(0);
}
private void assertEventsInAnyOrder(String... expectedEvents) {
if (events.size() < expectedEvents.length) {
fail("Expected " + StringUtils.join(expectedEvents, ",") + " but the event queue is: " + events);
}
List<String> list = events.subList(0, expectedEvents.length);
for (String expectedEvent : expectedEvents) {
assertTrue("Expected event " + expectedEvent + " was not found in queue", list.contains(expectedEvent));
}
for (@SuppressWarnings("UnusedDeclaration") String expectedEvent : expectedEvents) {
events.remove(0);
}
}
}