Call another method in Spring Aspect - spring-aop

I would like to convert a parameter and then call second method with this parameter.
The convention would be that there is always the same method overloaded with the specific type. The idea is to solve it with Spring AOP.
#Component
public class ExampleAspect {
#Around( "#annotation(Example)" )
public Object test( final ProceedingJoinPoint joinPoint ) throws Throwable {
final MethodSignature signature = (MethodSignature) joinPoint.getSignature();
final Method method = signature.getMethod();
final Example example = method.getAnnotation( Example.class );
final Object[] args = joinPoint.getArgs();
final String test = args[example.value()].toString();
final Bar bar = convertToBar(test)
args[example.value()] = bar;
//ReflectionUtils?
// call getBar(Bar bar)
//return joinPoint.proceed( args );
}
}
Here is the service
#Example(0)
public Object getBar(String test) {}
public Object getBar(Bar test) {}
Are there any better options or ideas?
EDIT:
Cannot inject the target bean, because this AOP should be used by more than specific target bean.
1 possible solution not sure if there is a smarter solution
#Around("#annotation(Example)")
public Object test(final ProceedingJoinPoint joinPoint) throws Throwable {
final MethodSignature signature = (MethodSignature) joinPoint.getSignature();
final Method method = signature.getMethod();
final Example example = method.getAnnotation(Example.class);
final Object[] args = joinPoint.getArgs();
final String bar = args[example.value()].toString();
final Bar aspectModelUrn = convertFromStringToBar(bar);
args[example.value()] = bar;
final Class<?>[] parameterTypes = method.getParameterTypes();
parameterTypes[example.value()] = Bar.class;
final Method newMethod = ReflectionUtils.findMethod(joinPoint.getTarget().getClass(), method.getName(), parameterTypes);
if (newMethod == null) {
throw new IllegalArgumentException("There is no method blubb. Have you forget to create the delegate method");
}
return newMethod.invoke(joinPoint.getTarget(), args);
}

Following code would provide a handle to the annotation and the target bean (for example , here TestComponent)
A call to the TestComponent.getBar() annotated with #Example would be intercepted and advised.
#Aspect
#Component
public class ExampleAspect {
#Around("#annotation(example) && target(bean)")
public Object test(final ProceedingJoinPoint joinPoint,Example example,TestComponent bean) throws Throwable {
String value = String.valueOf(example.value());
Bar bar = convertToBar(value);
bean.getBar(bar);
return joinPoint.proceed();
}
}
Do go through Spring AOP documentation : Passing Parameters to Advice for more details.
Note : For better performance it is a good idea to limit the scope of the expression as follows.
#Around("#annotation(example) && within(com.xyz.service..*) && target(bean)")
where com.xyz.service..* will limit the expression scope only to the beans with in the package com.xyz.service..* and its sub-packages.

Related

Camel set body to object with header expression

Is it possible to set a response body to an object and pass in a camel header property. I can achieve this in a processor but I'd rather do it in line of the route.
.setBody(constant(
new Foo()
.withOne("HelloWorld")
.withTwo(simple("Header property is ${header.uniqueProperty}").toString())
))
With the above code I get a response of:
<foo>
<one>HelloWorld</one>
<two>Header property is ${header.uniqueProperty}</two>
</foo>
Here is my POJO
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Foo {
private String one;
private String two;
public String getOne() {
return one;
}
public void setOne(final String one) {
this.one = one;
}
public String getTwo() {
return two;
}
public void setTwo(final String two) {
this.two = two;
}
public Foo withOne(final String one) {
setOne(one);
return this;
}
public Foo withTwo(final String two) {
setTwo(two);
return this;
}
}
constant() probably won't work for you, since you probably want this dynamically evaluated for every exchange that passes through. Since you need to set the body to a newly instantiated object, you need a mechanism that's capable of this. You mentioned you want to avoid a processor, but I'd like to point out how simple this could be done in the route:
.setBody(exchange -> new Foo()
.withOne("HelloWorld")
.withTwo(simple("Header property is " + exchange.getIn().getHeader("uniqueProperty")))
)
Edit: Actually this is not a processor. We're just passing a lambda (Function) to setBody().
If you're in a Spring environment, you could use spring expression language:
.setBody().spel("#{new Foo()" +
".withOne('HelloWorld')" +
".withTwo(simple('Header property is ' + request.headers.uniqueProperty))}");

How take always first parameter when requested array type param in spring mvc using #RequestParam

I wrote this code.
#GetMapping("/test")
public Response search(#RequestParam String value) {
System.out.println(value);
return new Response(value)
}
Some body request like
/test?value=a&value=b&value=c
value binded a,b,c
I want always bind first parmeter. Take a, ignore b, c.
Is there way using #RequestParam?
Or have to use HttpServletRequest and parsing parameter?
In this case you can use #RequestParam List<String> value instead of #RequestParam String value, and get the first value value.get(0) ignore the rest of them
For Example
http://rentacar.com/api/v1/search?make=audi&model=A8&type=6&type=11&type=12&color=RED&color=GREY
Method
public List<Vehicle> search(
#RequestParam(value="make", required=false) String make,
#RequestParam(value="model", required=false) String model,
#RequestParam(value="type", required=false) List<String> types,
#RequestParam(value="color", required=false) List<String> colors)
{
....
}
Great question!
I wrote this code to find out how this works. I included it in the test packages.
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ActiveProfiles("test")
public class ControllerTest {
#LocalServerPort
private int port;
private URL url;
#Autowired
private TestRestTemplate template;
#Before
public void setUp() throws Exception {
this.url = new URL("http://localhost:" + port + "/test?value=a&value=b&value=c");
}
#Test
public void getHello() throws Exception {
ResponseEntity<String> response = template.getForEntity(url.toString(),
String.class);
Assert.assertEquals(HttpStatus.OK, response.getStatusCode());
Assert.assertEquals(response.getBody(), "a");
System.out.println("response = " + response);
}
}
I then modified your code to accept an array of strings, and only pass the first element to your Response Constructor.
Notice the changes in your code in the signature and return statement.
#GetMapping("/test")
public String search(#RequestParam String[] value) {
System.out.println(value);
return new Response(value[0]);
}
With your test, you can now explore using a List type for your request param and quickly see how the behaviour has changed.

TestNg Assert.AssertTrue Always returns False - Selenium Webdriver

I have a util function as below:
public static boolean isWebElementEnabled(WebElement element) {
try {
return element.isEnabled();
} catch (Exception exx) {
return false;
}
}
public static boolean chkForThisElement(WebElement myElement) {
try {
return myElement.isDisplayed();
} catch (Exception e) {
// TODO Auto-generated catch block
return false;
}
}
I call it like this in the base class:
public static boolean isusernameBoxEnabled = isWebElementEnabled(unameBox);
public static boolean ispWordBoxEnabled = isWebElementEnabled(pwordBox);
public static boolean issubmitBtnEnabled = isWebElementEnabled(submitBtn);
public static boolean isctrsDrpdwnEnabled = isWebElementEnabled(multyCts);
When I test it in the Test class, it always returns false. I tried diff ways of testing for existence, but it only returns false.
#Test(priority=1)
public void verifyLoginpagecontrols() {
Assert.assertTrue(isusernameBoxEnabled);
Assert.assertTrue(ispWordBoxEnabled);
Assert.assertTrue(issubmitBtnEnabled);
Assert.assertTrue(isctrsDrpdwnEnabled);
}
i found a solution that works cool with Ff and Chromre driver nevertheless fails in Htmlunit driver.
Solution for the above problem -
// Initialize the home page elements and then check for assertions;
homePagePO searchPage = PageFactory.initElements(driver,
homePagePO.class);
Assert.assertTrue(chkForThisElement(searchPage.AccManagerHref));
Assert.assertTrue(chkForThisElement(searchPage.disHref));
Sorry to say but I find several things wrong with your code :-
You have not initialized the page factory. That is the reason why you are getting the null error.
In your comment, you have said that you are finding elements by using #findBy. But why have you decalared the WebElement as static?.
Why have you declared isusernameBoxEnabled and related boolean variables as global variables. You could use the isWebElementEnabled() function in your assert directly.
Basically your isWebElementEnabled() is not useful at all if you are using page factory.
Because the moment you use unameBox, selenium looks for the element in the webpage and if not found returns a noSuchElement Exception. So unameBox wont reach isWebElementEnabled() if it is not found in the webpage.
You said there is a base class and Test class. But I don't understand how your code works if there are different classes because you have not made a reference to static variable as Assert.assertTrue(baseClass.isusernameBoxEnabled). So I am assuming that you have only one class and different methods.
Try the following code :-
public class Base {
#FindBy()
WebElement unameBox;
#FindBy()
WebElement pwordBox;
#FindBy()
WebElement submitBtn;
#FindBy()
WebElement multyCts;
}
public class Test {
#Test(priority=1)
public void verifyLoginpagecontrols() {
//initialize page factory
Base base = PageFactory.initElements(driver, Base.class);
Assert.assertTrue(base.unameBox.isEnabled());
Assert.assertTrue(base.pwordBox.isEnabled());
Assert.assertTrue(base.submitBtn.isEnabled());
Assert.assertTrue(base.multyCts.isEnabled());
}
}
Hope this helps you.

Sending a complex object as a parameter in Resteasy

I am using resteasy, and till now I am just sending and receiving string as parameters and every thing was OK, but now I want to send a complex object ( List<Map<String, ObjectVal>> ) as one of my parameters. My objectVal class has two simple field ( id and value, with getters and setters).
I can find different question and answers for sending objects as parameters but all of them are missing something and not useful for me.
here is my functions with a simple string parameter
#GET
#Path("/isUserAuthorizedToDocument")
public Response isUserAuthorizedToDocumentService(
#QueryParam("userID") String userID){
.............
.............
}
and the client
private ClientRequest req =new ClientRequest(....url with path and ....)
req.queryParameter("userID", user.getUserId());
ClientResponse<Boolean> response = req.get(Boolean.class);
Now I want to send a parameter from my client in the form of List<Map<String,ObjectVal>> and recieve it in my rest function.
My ObjectVal class
#XmlRootElement(name = "objectValueDTO")
public class ObjectValueDTO implements Serializable {
/**
* Id for this class
*/
private static final long serialVersionUID = 164186789404269392L;
// Id on object type
private String objectTypeID = "";
// Selection
private String value = "";
/** Getter and Setters */
#XmlElement
public String getObjectTypeID() {
return objectTypeID;
}
public void setObjectTypeID(String objectTypeID) {
this.objectTypeID = objectTypeID;
}
#XmlElement
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
any help will be appreciated
I may be niave on this. But when you have to send complex parameters, you need to use PUT and send the parameters in the request.

Persistence with EJB3 doesn't work

I have a method to save a new object in an EJB bean. This method is called, without error, but nothing changes in the database. I can't understand why.
Here is the code:
#Stateless(name = "Ar", mappedName = "ManagementBean")
public class ManagementBean implements IManagementBeanLocal, IManagementBeanRemote {
...
#Override
public int storeRawSms(String raw, String requestUid, String text, String service, boolean correctlyAnalysed, Date receivedTimestamp,
boolean toBeAnalysed, String phoneNumber) {
// Get phone number, create if it dosn't exist
PhoneNumber pn = getOrCreatePhoneNumberPrivate(phoneNumber);
// Create rawSMS
RawSms rawSms = new RawSms(raw, requestUid, text, service, correctlyAnalysed, receivedTimestamp, toBeAnalysed, pn);
// Store and return result
em.persist(rawSms);
int result = rawSms.getId();
em.flush();
em.clear();
return result;
}
...
And the caller:
#PersistenceContext private EntityManager em;
...
int rawSmsIs = bean.storeRawSms(raw, requestUid, message, service, false, new Date(), true, sender);
Do you have an idea?
I see that you inject a reference to the EntityManager in the client (not sure why), but I don't see it in the session bean (maybe simply because you did not include the line in your message). Is it possible that you forgot to use the annotation #PersistenceContext in your stateless session bean?
Also, be careful: depending on the JPA implementation you are using and the generation strategy for the ids, you should call flush() before calling getId(). Indeed, if you let the DB generate your IDs, then you need a flush() to have this happen before the method returns the value.
Thanks, the prposed solution worked!
I use the container-managed transactions like this:
#Stateless(name = "Ar", mappedName = "ManagementBean")
#TransactionManagement(TransactionManagementType.CONTAINER)
public class ManagementBean implements IManagementBeanLocal, IManagementBeanRemote {
....
#Override
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public int storeRawSms(String raw, String requestUid, String text, String service, boolean correctlyAnalysed, Date receivedTimestamp, boolean toBeAnalysed, String phoneNumber) {
....
Thanks again!
It seems that your transaction never commited, so try changing transaction management:
#Stateless(name = "Ar", mappedName = "ManagementBean")
#TransactionManagement(TransactionManagementType.BEAN)
public class ManagementBean implements IManagementBeanLocal, IManagementBeanRemote {
#Resource
private UserTransaction utx;
#Override
public int storeRawSms(..) {
try {
utx.begin();
..
em.persist(rawSms);
int result = rawSms.getId();
utx.commit();
}
catch(Exception ex) {
//EXCEPTION HANDLING
utx.rollback();
}
}
}

Resources