/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.cxf.common.injection;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import javax.annotation.Resources;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.apache.cxf.resource.ResourceManager;
import org.apache.cxf.resource.ResourceResolver;
import org.easymock.EasyMock;
import org.junit.Assert;
import org.junit.Test;
public class ResourceInjectorTest extends Assert {
private static final String RESOURCE_ONE = "resource one";
private static final String RESOURCE_TWO = "resource two";
private static final String RESOURCE_THREE = "resource three";
private ResourceInjector injector;
public void setUpResourceManager(String pfx) {
ResourceManager resMgr = EasyMock.createMock(ResourceManager.class);
List<ResourceResolver> resolvers = new ArrayList<>();
resMgr.getResourceResolvers();
EasyMock.expectLastCall().andReturn(resolvers);
resMgr.resolveResource(pfx + "resource1", String.class, resolvers);
EasyMock.expectLastCall().andReturn(RESOURCE_ONE);
resMgr.resolveResource("resource2", String.class, resolvers);
EasyMock.expectLastCall().andReturn(RESOURCE_TWO);
resMgr.resolveResource("resource3", CharSequence.class, resolvers);
EasyMock.expectLastCall().andReturn(RESOURCE_THREE);
EasyMock.replay(resMgr);
injector = new ResourceInjector(resMgr);
}
@Test
public void testFieldInjection() {
setUpResourceManager(FieldTarget.class.getCanonicalName() + "/");
doInjectTest(new FieldTarget());
}
@Test
public void testFieldInSuperClassInjection() {
setUpResourceManager("org.apache.cxf.common.injection.FieldTarget/");
doInjectTest(new SubFieldTarget());
}
@Test
public void testSetterInSuperClassInjection() {
setUpResourceManager("org.apache.cxf.common.injection.SetterTarget/");
doInjectTest(new SubSetterTarget());
}
@Test
public void testSetterInjection() {
setUpResourceManager(SetterTarget.class.getCanonicalName() + "/");
doInjectTest(new SetterTarget());
}
@Test
public void testProxyInjection() {
setUpResourceManager(SetterTarget.class.getCanonicalName() + "/");
doInjectTest(getProxyObject(), SetterTarget.class);
}
@Test
public void testEnhancedInjection() {
setUpResourceManager(FieldTarget.class.getCanonicalName() + "/");
doInjectTest(getEnhancedObject());
}
@Test
public void testClassLevelInjection() {
setUpResourceManager("");
doInjectTest(new ClassTarget());
}
@Test
public void testResourcesContainer() {
setUpResourceManager("");
doInjectTest(new ResourcesContainerTarget());
}
@Test
public void testPostConstruct() {
setUpResourceManager(SetterTarget.class.getCanonicalName() + "/");
SetterTarget target = new SetterTarget();
doInjectTest(target);
assertTrue(target.injectionCompleteCalled());
}
@Test
public void testPreDestroy() {
injector = new ResourceInjector(null, null);
SetterTarget target = new SetterTarget();
injector.destroy(target);
assertTrue(target.preDestroyCalled());
}
private void doInjectTest(Target target) {
doInjectTest(target, target.getClass());
}
private void doInjectTest(Target target, Class<?> clazz) {
injector.inject(target, clazz);
injector.construct(target);
assertNotNull(target.getResource1());
assertEquals(RESOURCE_ONE, target.getResource1());
assertNotNull(target.getResource2());
assertEquals(RESOURCE_TWO, target.getResource2());
assertNotNull(target.getResource3());
assertEquals(RESOURCE_THREE, target.getResource3());
}
private Target getProxyObject() {
return (Target)Proxy.newProxyInstance(ISetterTarget.class.getClassLoader(),
new Class[] {ISetterTarget.class},
new ProxyClass(new SetterTarget()));
}
private FieldTarget getEnhancedObject() {
Enhancer e = new Enhancer();
e.setSuperclass(FieldTarget.class);
e.setCallback(new CallInterceptor());
return (FieldTarget)e.create();
}
}
interface Target {
String getResource1();
String getResource2();
CharSequence getResource3();
}
class CallInterceptor implements MethodInterceptor {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
Object retValFromSuper = null;
if (!Modifier.isAbstract(method.getModifiers())) {
retValFromSuper = proxy.invokeSuper(obj, args);
}
return retValFromSuper;
}
}
class FieldTarget implements Target {
@Resource
private String resource1;
@Resource(name = "resource2")
private String resource2foo;
@Resource(name = "resource3")
private CharSequence resource3foo;
public String getResource1() {
return resource1;
}
public String getResource2() {
return resource2foo;
}
public CharSequence getResource3() {
return resource3foo;
}
public String toString() {
return "[" + resource1 + ":" + resource2foo + ":" + resource3foo + "]";
}
}
class SubFieldTarget extends FieldTarget {
}
class SubSetterTarget extends SetterTarget {
}
interface ISetterTarget extends Target {
void setResource1(String argResource1);
void setResource2(String argResource2);
void setResource3(CharSequence argResource3);
}
class ProxyClass implements InvocationHandler {
Object obj;
ProxyClass(Object o) {
obj = o;
}
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
Object result = null;
try {
Class<?>[] types = new Class[0];
if (args != null) {
types = new Class[args.length];
for (int i = 0; i < args.length; i++) {
types[i] = args[i].getClass();
if ("setResource3".equals(m.getName()) && types[i].equals(String.class)) {
types[i] = CharSequence.class;
}
}
}
Method target = obj.getClass().getMethod(m.getName(), types);
result = target.invoke(obj, args);
} catch (InvocationTargetException e) {
// Do nothing here
} catch (Exception eBj) {
eBj.printStackTrace();
}
return result;
}
}
class SetterTarget implements Target {
private String resource1;
private String resource2;
private CharSequence resource3;
private boolean injectionCompletePublic;
private boolean injectionCompletePrivate;
private boolean preDestroy;
private boolean preDestroyPrivate;
public final String getResource1() {
return this.resource1;
}
@Resource
public final void setResource1(final String argResource1) {
this.resource1 = argResource1;
}
public final String getResource2() {
return this.resource2;
}
@Resource(name = "resource2")
public void setResource2(final String argResource2) {
this.resource2 = argResource2;
}
public final CharSequence getResource3() {
return this.resource3;
}
@Resource(name = "resource3")
public void setResource3(final CharSequence argResource3) {
this.resource3 = argResource3;
}
@PostConstruct
public void injectionIsAllFinishedNowThankYouVeryMuch() {
injectionCompletePublic = true;
// stick this here to keep PMD happy...
injectionIsAllFinishedNowThankYouVeryMuchPrivate();
}
@PostConstruct
private void injectionIsAllFinishedNowThankYouVeryMuchPrivate() {
injectionCompletePrivate = true;
}
@PreDestroy
public void preDestroyMethod() {
preDestroy = true;
}
@PreDestroy
private void preDestroyMethodPrivate() {
preDestroyPrivate = true;
}
public boolean injectionCompleteCalled() {
return injectionCompletePrivate && injectionCompletePublic;
}
public boolean preDestroyCalled() {
return preDestroy && preDestroyPrivate;
}
// dummy method to access the private methods to avoid compile warnings
public void dummyMethod() {
preDestroyMethodPrivate();
setResource2("");
}
}
//CHECKSTYLE:OFF
@Resource(name = "resource1")
class ClassTarget implements Target {
@Resource(name = "resource3")
public CharSequence resource3foo;
@Resource(name = "resource2")
public String resource2foo;
private String res1;
public final void setResource1(String res) {
res1 = res;
}
public final String getResource1() {
return res1;
}
public final String getResource2() {
return resource2foo;
}
public final CharSequence getResource3() {
return resource3foo;
}
}
@Resources({@Resource(name = "resource1"),
@Resource(name = "resource2"),
@Resource(name = "resource3") })
class ResourcesContainerTarget implements Target {
private String res1;
private String resource2;
private CharSequence resource3;
public final void setResource1(String res) {
res1 = res;
}
public final String getResource1() {
return res1;
}
public final String getResource2() {
return resource2;
}
public final CharSequence getResource3() {
return resource3;
}
}