Is it possible to interrupt invoking the actual method from within the aspect execution?
For example:
public class CheckPermissionAspect {
#Around("#annotation(CheckPermission)")
public Object methodLogging( ProceedingJoinPoint joinPoint) throws Throwable {
// before method execution
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
log.info("Enter ==> " + signature.getMethod().getName());
if ( getPermission( principal.getName()) == false ) {
// break the execution of actual method
Object result = null; // ???
log.info("Break ==> " + signature.getMethod().getName());
} else {
// invoke the actual method
Object result = joinPoint.proceed();
// after method execution
log.debug("Result: " + result);
log.info("Leave ==> " + signature.getMethod().getName());
}
return result;
}
}
To set Object result = null; does not work.
Thank you for any help.
From Spring Reference documentation : Around Advice
Within the body of the advice method, you must invoke proceed() on the
ProceedingJoinPoint in order for the underlying method to run
If joinPoint.proceed() is removed from the code or an exception is thrown before joinPoint.proceed() statement or a return is issued before joinPoint.proceed() statement , the execution of the underlying method would get interrupted.
Update : to answer the comment
Spring AOP is proxy based. To understand this concept better , do read through : Understanding AOP proxies
An advice is placed between the calling method and the target. All calls to the target method gets intercepted and advised.
You could throw an exception upon validation ( Do note that the exception type thrown should match the exception type of the target method . Go through this Q&A).
The exception/return value from the advise would reach back the calling method .
Related
I have client API jar which internally makes external calls and throws single generic Service exception in case of issues. I have written hystrix wrapper over the API calls. There are cases like "user not found" returning exception. Though the call was successful and service responded with valid response, the hystrix is treating it as a failure. I know that we can ignore the exception in Hystrix; but it will whitelist the only exception thrown by service calls. Is there a way to selectively ignore exception thrown by the service calls based on message in exception or http status code or something?
If the external service throw different exceptions in different cases, then you can probably ignore those exceptions like this
#HystrixCommand(ignoreExceptions = {SomeException.class})
But if you have to ignore exceptions bases on error message then the best way to tackle this is put a try catch around your external call. And in the catch block check if it is one of those exceptions which needs to be ignored. If so don't do anything. If not rethrow this exception. Something like this will do. More info about HystrixBadRequestException
#HystrixCommand(fallbackMethod = "fallBackMethod", groupKey = "CircuitBreaker", commandKey = "somekey", threadPoolKey = "somekey",
commandProperties = {
#HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "10000"),
#HystrixProperty(name = "execution.timeout.enabled", value = "false"),
#HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"),
#HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "1200000"),
},
threadPoolProperties = {
#HystrixProperty(name = "coreSize", value = "30"),
#HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "180000")
})
public void someMethod(....){
try {
// Call external service
} catch(Exception e) {
if(exception to be ignored)
throw new HystrixBadRequestException("Some message", e);
else
throw e
}
}
This is more a general, whats the best practice question...
I have a few processes where the consumer template has been used to read a directory (or a MQ queue) for whatever is available and then stop itself, the entire route-set it calls is created programmatically based of a few parameters
So using the consumer template method below... Is there a way to assign
A filter operation programmatically (ie, if i want to filter out certain files from the below, its easy if its through a standard route... (through .filter) but at the moment, i have no predefined beans, so adding #filter=filter to the EIP is not really an option).
An aggregation function from inside my while loop. (while still using the template).
#Override
public void process(Exchange exchange) throws Exception {
getConsumer().start();
int exchangeCount = 0;
while (true) {
String consumerEp = "file:d://directory?delete=true&sendEmptyMessageWhenIdle=true&idempotent=false";
Exchange fileExchange = getConsumer().receive(consumerEp);
if (fileExchange == null || fileExchange.getIn()==null || fileExchange.getIn().getHeader(CAMEL_FILE_NAME)==null) {
break;
}
exchangeCount++;
Boolean batchStatus = (Boolean) fileExchange.getProperty(PROP_CAMEL_BATCH_COMPLETE);
LOG.info("---PROCESSING : " + fileExchange.getIn().getHeader(CAMEL_FILE_NAME));
getProducer().send("direct:some-other-process", fileExchange);
//Get the CamelBatchComplete Property to establish the end of the batch, and not cycle through twice.
if(batchStatus!=null && batchStatus==true){
break;
}
}
// Stop the consumer service
getConsumer().stop();
LOG.info("End Group Operation : Total Exchanges=" + exchangeCount);
}
I have a mapper method like this :
#InsertProvider(class=com.something.class, method="doSomething")
public void insertSomething(Set<Integer> set, int guideId);
and in the something class, I have a method :
public String doSomething(Set<Integer> set, int guideId){
// do something and returna a query
}
It gives me an error :
Error creating SqlSource for SqlProvider. Method 'doSomething' not
found in SqlProvider 'com.something.class'
When I debugged the issue.. I found that in the constructor of ProviderSqlResource, it throws this exception if the no. of arguments are 2 or more. I can't think of any reason why they would do that. What's the workaround ?
Here is the method :
public ProviderSqlSource(Configuration config, Object provider) {
String providerMethodName = null;
try {
this.sqlSourceParser = new SqlSourceBuilder(config);
this.providerType = (Class<?>) provider.getClass().getMethod("type").invoke(provider);
providerMethodName = (String) provider.getClass().getMethod("method").invoke(provider);
for (Method m : this.providerType.getMethods()) {
if (providerMethodName.equals(m.getName())) {
if (m.getParameterTypes().length < 2
&& m.getReturnType() == String.class) {
this.providerMethod = m;
this.providerTakesParameterObject = m.getParameterTypes().length == 1;
}
}
}
} catch (Exception e) {
throw new BuilderException("Error creating SqlSource for SqlProvider. Cause: " + e, e);
}
if (this.providerMethod == null) {
throw new BuilderException("Error creating SqlSource for SqlProvider. Method '"
+ providerMethodName + "' not found in SqlProvider '" + this.providerType.getName() + "'.");
}
}
It turns out that we can pass any number of arguments in methods annotated with SelectProvider (or any other provider). But the method actually providing the query (doSomething, in my case) will actually receive a single argument i.e. a map wrapper around all the arguments. For example, if the arguments were as in the questions above (a set and an integer), we can access them from the map (called parametersMap) as follows :
Set<Integer> nums = (Set<Integer>) parametersMap.get("0");
int groupId = (Integer) parametersMap.get("1");
The first parameter is keyed with "0" and the second with "1" and so on.
IMHO, the arguments should have been keyed with their names so that we could do something like :
parametersMap.get("set");
parametersMap.get("guideId")
It would probably have been more clean. But that's how its implemented.
For providing multiple arguments use #Param tag in the arguments.
I am working with mule <cxf:proxy-service> and need to extract the web service method name to attach to message for later use.
We've a service proxy class implementing Callable interface. Initially we tried to get operation name like this:
public Object onCall(MuleEventContext eventContext) throws Exception {
try {
MuleMessage inboundMessage = eventContext.getMessage();
Set<String> props = inboundMessage.getInvocationPropertyNames();
System.out.println("CXF invocation properties ==> " + props);
System.out.println("CXF invocation property ==> " + inboundMessage.getInvocationProperty("cxf_operation"));
but the above code gives incorrect operation name. (We've 4 operations in service and it always give the 2nd operation name). Below is the mule flow used for this:
<flow name="proxyService">
<http:inbound-endpoint address="${some.address}"
exchange-pattern="request-response">
<cxf:proxy-service wsdlLocation="classpath:abc.wsdl"
namespace="http://namespace"
service="MyService">
</cxf:proxy-service>
</http:inbound-endpoint>
<component class="com.services.MyServiceProxy" />
So, I resorted to write an inbound cxf interceptor to extract the operation name. I wrote below interceptor which works fine with <cxf:jaxws-service> but not with <cxf:proxy-service> element.
Here is my interceptor:
public class GetCXFOperation extends AbstractPhaseInterceptor<Message> {
public GetCXFOperation() {
super(Phase.PRE_INVOKE);
}
#Override
public void handleMessage(Message message) throws Fault {
Exchange exchange = message.getExchange();
Endpoint ep = exchange.get(Endpoint.class);
OperationInfo op = exchange.get(OperationInfo.class);
if(op != null){
System.out.println("Operation Name: " + op.getName().getLocalPart());
} else{
Object nameProperty = exchange.get("org.apache.cxf.resource.operation.name");
if(nameProperty != null)
System.out.println(nameProperty.toString());
}
}
}
Seeking guidance as to how to extract operation name in <cxf:proxy-service>? Is there an easy mule way of getting correct answer? Or is there a different phase in which I should be invoking my interceptor? What phases work with <cxf:proxy-service>
I have even used List but still, I get null pointer but if I use livedata it updates successfully inside the for a loop. It doesn't return null. Why is it only list or Arraylist which returns null
fun List(): ArrayList<Bank> {
val banklist = ArrayList<Bank>()
val reference = FirebaseDatabase.getInstance().reference
.child("groups")
.child(group.group_id)
.child("financials")
.child("cash")
.child("bank")
reference.addValueEventListener(object : ValueEventListener {
override fun onCancelled(p0: DatabaseError) {
}
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (singleSnapshot in dataSnapshot.children) {
Log.d(
"Data", "banks found: "
+ singleSnapshot.value
)
val bank = singleSnapshot.getValue(Bank::class.java)
banklist.add(bank!!)
bankListLivedata.value = banklist //updates perfectly
}
Log.d(
"Data", "banksList1 ${banklist[0].bank_account} "//This is printing in the log
)
}
}
)
Log.d(
"Data", "banksList2 ${banklist[0].bank_account} " //This is throwing null pointer exception
)
return banklist //this is null
//if I return banklistLivedata it works perfectly and doesn't throw null
}
The NPE thrown in your logging is because banklist is modified in a change listener.
Log.d(
"Data", "banksList2 ${banklist[0].bank_account} " //This is throwing null pointer exception
)
When no change was done, or inside the listener the iterable dataSnapshot.children is empty - banklist stays empty. So when you call list[index] it will return null since nothing was found and it will throw a NPE because you call .bank_account on null.
return banklist //this is null
I'm pretty sure that banklist at the end of your method is not null. The part is just never reached because the code before throws an Exception.
The solution is to make sure that you are checking if the list is empty before attempting to log, or better yet use one of Kotlin's utility methods (like firstOrNull()) to retrieve the first element of the list:
Log.d("Data", "banksList2 ${banklist.firstOrNull()?.bank_account} ")
I'd also suggest doing the same for the logging inside the listener:
Log.d("Data", "banksList1 ${banklist.firstOrNull()?.bank_account} ")