Create context with Java reflection and invoke method - java

Exception class
public BusinessException(ErrorCodeEnum errorCodeEnum) {
super(errorCodeEnum.getMessage());
this.errorCodeEnum = errorCodeEnum;
this.errorMessage = errorCodeEnum.getMessage();
}
ErrorCodeEnum class
public String getMessage() {
MessageUtil messageUtil = SpringContextUtil.getBean(MessageUtil.class);
return messageUtil.get(this.message);
}
SpringContextUtil class
public static Object getBean(String name) {
return appContext.getBean(name);
}
public static <T> T getBean(Class<T> clazz) {
return appContext.getBean(clazz);
}
public static synchronized void setContext(ApplicationContext applicationContext) {
appContext = applicationContext;
}
#Override
#Autowired
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
setContext(applicationContext);
}
To test an exception of BusinessException class
I need to invoke getBean method.
what is the correct way to invoke the method?

Related

How to add annotation to method runtime? Spring AOP

I have a problem with the annotated method in interface
public interface CsvImportService {
#Import
void importFile(Long userId, String organizationId, MultipartFile file, String charset) throws Exception;
}
Implementation of interface
#Service
public class CsvImportServiceImpl implements CsvImportService {
#Override
public void importFile(Long userId, String organizationId, MultipartFile file, String charset) {
...
}
}
I tried to handle it by Spring AOP
#Slf4j
#Aspect
#Component
public class ImportAspect {
#AfterReturning(pointcut = "#annotation(com.backend.annotations.Import)")
public void handleImport(JoinPoint joinPoint) throws Throwable {
LOGGER.info("handle");
}
}
But found out that annotation doesn't go to the impl, so I realized that I want to add the annotation to the realization method and started to write BPP
#Component
public class ImportAnnotationBeanPostProcessor implements BeanPostProcessor {
Map<String, Class<?>> map = new HashMap<>();
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Class<?> beanClass = bean.getClass();
if (CsvImportService.class.isAssignableFrom(beanClass)) {
map.put(beanName, beanClass);
}
return bean;
}
#SneakyThrows
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
Class<?> beanClass = map.get(beanName);
if (beanClass != null) {
for (Method declaredMethod : CsvImportService.class.getDeclaredMethods()) {
if (declaredMethod.isAnnotationPresent(Import.class)) {
Method importMethod = beanClass.getDeclaredMethod(declaredMethod.getName(), declaredMethod.getParameterTypes());
//TODO Add annotation to method
}
}
}
return bean;
}
}
And didn't find a reflection method that adds an annotation to the method.
how am I supposed to do that?

Jmockit and #PostConstruct bean

I have created a bean with method that I want to test. Unfortunately it's a bean with a PostConstruct annotation in it. I don't want to call the PostConstruct method.
How can I do this?
I've tried 2 different ways (as shown in the example below) but none working; init() still gets called.
Can someone please give me a detailed example of how to do this?
DirBean.java
#Singleton
#Startup
public class DirBean implements TimedObject {
#Resource
protected TimerService timer;
#PostConstruct
public void init() {
// some code I don't want to run
}
public void methodIwantToTest() {
// test this code
}
}
MyBeanTest.java
public class MyBeanTest {
#Tested
DirBean tested;
#Before
public void recordExpectationsForPostConstruct() {
new Expectations(tested) {
{
invoke(tested, "init");
}
};
}
#Test
public void testMyDirBeanCall() {
new MockUp<DirBean>() {
#Mock
void init() {
}
};
tested.methodIwantToTest();
}
}
MyBeanTest2.java (WORKS)
public class MyBeanTest2 {
#Tested
DirBean tested;
#Before
public void recordExpectationsForPostConstruct() {
new MockUp<DirBean>() {
#Mock
void init() {}
};
}
#Test
public void testMyDirBeanCall() {
tested.methodIwantToTest();
}
}
MyBeanTest3.java (WORKS)
public class MyBeanTest3 {
DirBean dirBean = null;
#Mock
SubBean1 mockSubBean1;
#Before
public void setupDependenciesManually() {
dirBean = new DirBean();
dirBean.subBean1 = mockSubBean1;
}
#Test
public void testMyDirBeanCall() {
dirBean.methodIwantToTest();
}
}
MyBeanTest4.java (FAILS with NullPointerException on invoke())
public class MyBeanTest4 {
#Tested
DirBean tested;
#Before
public void recordExpectationsForCallsInsideInit() {
new Expectations(tested) {
{
Deencapsulation.invoke(tested, "methodCalledfromInit", anyInt);
}
};
}
#Test
public void testMyDirBeanCall() {
tested.methodIwantToTest();
}
}
Move definition of MockUp type to #Before method:
public class MyBeanTest {
#Tested
DirBean tested;
#Before
public void recordExpectationsForPostConstruct() {
new MockUp<DirBean>() {
#Mock
void init() {
}
};
}
#Test
public void testMyDirBeanCall() {
tested.methodIwantToTest();
}
}

Custom Spring Bean Parameters

I'm using the Spring Akka example posted on activator to create Spring managed bean actors. This is the code I'm currently using including a demo class:
#Component
class Test extends UntypedActor {
#Autowired
protected ObjectMapper objectMapper;
protected final Account account;
protected final Order order;
public Test(Account account, Order order) {
this.account = account;
this.order = order;
}
#Override
public void onReceive(Object message) throws Exception {
if (message instanceof SomeCommand) {
// Do something using the order and the account;
} else if (message instanceof FooCommand) {
// More stuff
}
}
}
#Component
public class SpringExtension extends AbstractExtensionId<SpringExtensionImpl> implements ExtensionIdProvider {
#Autowired
private ApplicationContext applicationContext;
#Override
public SpringExtensionImpl createExtension(ExtendedActorSystem system) {
return applicationContext.getBean(SpringExtensionImpl.class);
}
#Override
public ExtensionId<? extends Extension> lookup() {
return applicationContext.getBean(SpringExtension.class);
}
}
#Component
public class SpringExtensionImpl implements Extension {
#Autowired
private ApplicationContext applicationContext;
public Props props(String actorBeanName) {
return Props.create(SpringActorProducer.class, applicationContext, actorBeanName);
}
}
public class SpringActorProducer implements IndirectActorProducer {
private final ApplicationContext applicationContext;
private final String actorBeanName;
public SpringActorProducer(ApplicationContext applicationContext, String actorBeanName) {
this.applicationContext = applicationContext;
this.actorBeanName = actorBeanName;
}
#Override
public Actor produce() {
return (Actor) applicationContext.getBean(actorBeanName);
}
#Override
public Class<? extends Actor> actorClass() {
return (Class<? extends Actor>) applicationContext.getType(actorBeanName);
}
}
Now my question is, how do instantiate an actor with custom constructor arguments. I have thought about using a factory or setter methods but I don't think this is an option since the underlying Actor class is not accessible I believe. Any input on this matter is greatly appreciated. If something is now clear, please post a comment.
PS. If you believe my there is an error in my code or there is a better way of going about it, please do tell me! I have little experience with Spring and Akka combined so any advice is appreciated.
You could pass the additional arguments as varargs (Object...) to SpringExtensionImpl and SpringActorProducer. So your code would look like this:
#Component
public class SpringExtensionImpl implements Extension {
#Autowired
private ApplicationContext applicationContext;
public Props props(String actorBeanName, Object... args) {
return (args != null && args.length > 0) ?
Props.create(SpringActorProducer.class,
applicationContext,
actorBeanName, args) :
Props.create(SpringActorProducer.class,
applicationContext,
actorBeanName);
}
}
public class SpringActorProducer implements IndirectActorProducer {
private final ApplicationContext applicationContext;
private final String actorBeanName;
private final Object[] args;
public SpringActorProducer(ApplicationContext applicationContext, String actorBeanName) {
this.applicationContext = applicationContext;
this.actorBeanName = actorBeanName;
this.args = null;
}
public SpringActorProducer(ApplicationContext applicationContext, String actorBeanName, Object... args) {
this.applicationContext = applicationContext;
this.actorBeanName = actorBeanName;
this.args = args;
}
#Override
public Actor produce() {
return args == null ?
(Actor) applicationContext.getBean(actorBeanName):
(Actor) applicationContext.getBean(actorBeanName, args);
}
#Override
public Class<? extends Actor> actorClass() {
return (Class<? extends Actor>) applicationContext.getType(actorBeanName);
}
}
You can then create your Test actor like this:
SpringExtensionImpl springExtensionImpl;
actorSystem.actorOf(springExtensionImpl.create(Test.class, account, order));

Use spring beans from Serializable objects

I need to execute task on remote machine.
This task is dummy Runnable or Callable and Serializable to be transferred to remote host, deserialized and executed there.
I need to use spring beans from that task to execute it on remote machine.
What could be the elegant way to 'serialize' bean name when task is serialized on client machine and 'deserialize' real bean while deserialization on remote machine?
Any other solutions?
private static class MyCommand implements Callable<String>, Serializable {
private static final long serialVersionUID = 8980820796677215627L;
private transient SpringBean springBean;
private String bar;
public InitDoneRemoteCommand(SpringBean springBean, String bar) {
this.springBean = springBean;
this.bar = bar;
}
#Override
public String call() {
return springBean.foo(bar);
}
private void writeObject(java.io.ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
out.writeObject(getBeanName(springBean));
}
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
springBean = getBean((String) in.readObject());
}
}
SpringContext .java
#Resource
public class SpringContext implements ApplicationContextAware, BeanPostProcessor, BundleContextAware, ServiceListener {
private static ApplicationContext applicationContext;
private static BundleContext bundleContext;
private static Map<Object, String> springBeanToName = synchronizedMap(new WeakHashMap<Object, String>());
private static Map<String, ServiceReference> osgiNameToServiceReference = synchronizedMap(new WeakHashMap<String, ServiceReference>());
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public static BundleContext getBundleContext() {
return bundleContext;
}
#SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
ServiceReference ref = osgiNameToServiceReference.get(name);
if (ref != null)
return (T) bundleContext.getService(ref);
return (T) applicationContext.getBean(name);
}
public static String getBeanName(Object bean) {
if (isOsgiBean(bean))
return getOsgiBeanName(bean);
return springBeanToName.get(bean);
}
public static boolean isOsgiBean(Object bean) {
return bean instanceof ImportedOsgiServiceProxy || bean instanceof ServiceReferenceProxy || bean instanceof ServiceReference;
}
public static String getOsgiBeanName(Object proxy) {
if (proxy == null)
return null;
ServiceReference serviceReference = null;
if (proxy instanceof ImportedOsgiServiceProxy)
serviceReference = ((ImportedOsgiServiceProxy) proxy).getServiceReference().getTargetServiceReference();
else if (proxy instanceof ServiceReferenceProxy)
serviceReference = ((ServiceReferenceProxy) proxy).getTargetServiceReference();
else if (proxy instanceof ServiceReference)
serviceReference = ((ServiceReference) proxy);
if (serviceReference != null)
return (String) serviceReference.getProperty(OSGI_BEAN_NAME_PROPERTY);
throw new IllegalArgumentException(proxy.toString());
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
springBeanToName.put(bean, beanName);
return bean;
}
#Override
public void serviceChanged(ServiceEvent event) {
ServiceReference ref = event.getServiceReference();
String name = getOsgiBeanName(ref);
if (event.getType() == ServiceEvent.REGISTERED)
osgiNameToServiceReference.put(name, ref);
else if (event.getType() == ServiceEvent.UNREGISTERING)
osgiNameToServiceReference.remove(name);
}
#Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
SpringContext.applicationContext = context;
}
#Override
public void setBundleContext(BundleContext bundleContext) {
SpringContext.bundleContext = bundleContext;
bundleContext.addServiceListener(this);
}
}
If you have access to the ApplicationContext you can ask it to create the instance for you, which will e.g. enable autowiring:
appContext.getAutowireCapableBeanFactory().createBean(
beanClass,
AbstractBeanDefinition.AUTOWIRE_BY_TYPE,
true)
A more elegant way would be to annotate the class with #Configurable, described here.

ThreadWeaver always throws IllegalArgumentException

I am trying to use Google ThreadWeaver to write a unit test for concurrent code. No matter what I do, I will get an IllegalArgumentException. I am still working with an example, but even that does not work. This is what I tried:
public class ExampleTest {
public static class ExampleMain implements MainRunnable<Example> {
private Example example;
#Override
public Class<Example> getClassUnderTest() {
return Example.class;
}
#Override
public String getMethodName() {
return null;
}
#Override
public Method getMethod() throws NoSuchMethodException {
return null;
}
#Override
public void initialize() throws Exception {
example = new Example();
}
#Override
public Example getMainObject() {
return example;
}
#Override
public void terminate() throws Exception {
}
#Override
public void run() throws Exception {
example.test("second");
}
}
public static class ExampleSecondary implements SecondaryRunnable<Example, ExampleMain> {
private ExampleMain exampleMain;
#Override
public void initialize(ExampleMain main) throws Exception {
exampleMain = main;
}
#Override
public void terminate() throws Exception {
}
#Override
public boolean canBlock() {
return false;
}
#Override
public void run() throws Exception {
exampleMain.getMainObject().test("main");
}
}
public static class Example {
private List<String> list = new ArrayList<String>();
public String test(String s) {
System.out.println("1" + s);
list.add(s);
System.out.println("2" + s);
return list.get(0);
}
}
#Test
public void testThreadWeaver() throws Exception {
ClassInstrumentation instrumentation = Instrumentation.getClassInstrumentation(Example.class);
Method tested = Example.class.getDeclaredMethod("test", String.class);
Method breakpoint = List.class.getDeclaredMethod("add", Object.class);
CodePosition codePosition = instrumentation.afterCall(tested, breakpoint);
InterleavedRunner.interleave(new ExampleMain(), new ExampleSecondary(), Arrays.asList(codePosition)).throwExceptionsIfAny();
}
}
The stack trace says:
java.lang.IllegalArgumentException: Class Example is not instrumented
at
com.google.testing.threadtester.CallLoggerFactory.getClassInstrumentation(CallLoggerFactory.java:108)
at
com.google.testing.threadtester.Instrumentation.getClassInstrumentation(Instrumentation.java:65)
at MyTest.testThreadWeaver(MyTest.java:92
I followed the instructions at the official Google code webpage, but it does not seem to work. Any ideas?
ThreadWeaver needs to instrument your classes in order to add breakpoints to your methods. Therefore, you cannot run the tests with JUnit directly but you must run your test from a specific test runner. For your case this would be ThreadedTestRunner. The actual test methods must then be annotated with #ThreadedTest instead of #Test. This should work:
#Test
public void startTest() throws Exception {
new ThreadedTestRunner().runTests(getClass(), Example.class);
}
#ThreadedTest
public void testThreadWeaver() throws Exception {
// here comes your test
}

Categories