I'd like to write an interceptor for the Apache CXF JAX-RS implementation that inspects the target service/method for a particular annotation and does some special processing for that annotation.
I can't seem to find anything in the interceptor documentation that describes how to do this. Does anyone have any ideas?
Thanks!
If the interceptor runs fairly late in the chain (like the USER_LOGICAL
phase), you should be able to do something like:
Exchange exchange = msg.getExchange();
BindingOperationInfo bop = exchange.get(BindingOperationInfo.class);
MethodDispatcher md = (MethodDispatcher)
exchange.get(Service.class).get(MethodDispatcher.class.getName());
Method meth = md.getMethod(bop);
That should give you the Method that was bound in so you can get the declared
class or the annotations, etc...
Ah. I didn't specify that I was using the JAX-RS part of CXF; not sure if that impacts Daniel Kulp's answer but his solution didn't actually work for me. I believe it is because CXF does things differently when handling JAX-RS.
I came across the source for CXF's [JAXRSInInterceptor][1] and I saw in that code that this interceptor is putting the method info into the Exchange object like so:
message.getExchange().put(OperationResourceInfo.class, ori);
...during the UNMARSHAL phase, which according to the CXF interceptor docs happens before the *_LOGICAL phase. So by writing an Interceptor that handles the USER_LOGICAL phase I can do:
message.getExchange().get(OperationResourceInfo.class)
...to get access in there to the Method and Class<?> of the Service handling the call!
Building off the original interrogator's answer, I came up with this
public UserContextInterceptor() {
super(Phase.USER_LOGICAL);
}
#Override
public void handleMessage(Message message) {
if(StringUtils.isEmpty(getHeader("some-header-name", message))) {
final Method method = getTargetMethod(message);
if(isAnnotated(method.getDeclaringClass().getAnnotations()) || isAnnotated(method.getAnnotations())) {
final Fault fault = new Fault(new LoginException("Missing user id"));
fault.setStatusCode(HttpServletResponse.SC_UNAUTHORIZED);
throw fault;
}
}
}
private static Method getTargetMethod(Message message) {
final Exchange exchange = message.getExchange();
final OperationResourceInfo resource = exchange.get(OperationResourceInfo.class);
if(resource == null || resource.getMethodToInvoke() == null) {
throw new AccessDeniedException("Method is not available");
}
return resource.getMethodToInvoke();
}
private static boolean isAnnotated(Annotation[] annotations) {
for(Annotation annotation : annotations) {
if(UserRequired.class.equals(annotation.annotationType())) {
return true;
}
}
return false;
}
It has been quite some time since the accepted answer. But there are a few supporting abstractions provided in the
cxf-rt-core-2.7.3.jar
One in there that is provided is org.apache.cxf.interceptor.security.AbstractAuthorizingInInterceptor
This sample excerpt from the source might be a good reference:
protected Method getTargetMethod(Message m) {
BindingOperationInfo bop = m.getExchange().get(BindingOperationInfo.class);
if (bop != null) {
MethodDispatcher md = (MethodDispatcher)
m.getExchange().get(Service.class).get(MethodDispatcher.class.getName());
return md.getMethod(bop);
}
Method method = (Method)m.get("org.apache.cxf.resource.method");
if (method != null) {
return method;
}
throw new AccessDeniedException("Method is not available : Unauthorized");
}
Related
Background:
We are implementing a signed request mechanism for communication between services. Part of that process generates a digest on the contents of the request body. To validate the body on receipt, we re-generate the digest at the receiver and compare. It's pretty straight-forward stuff.
#PreMatching
#Priority(Priorities.ENTITY_CODER)
public class DigestValidationFilter implements ContainerRequestFilter {
private final DigestGenerator generator;
#Inject
public DigestValidationFilter(DigestGenerator generator) {
this.generator = generator;
}
#Override
public void filter(ContainerRequestContext context) throws IOException {
if (context.hasEntity() && context.getHeaderString(Headers.DIGEST) != null) {
String digest = context.getHeaderString(Headers.DIGEST);
ByteArrayOutputStream body = new ByteArrayOutputStream();
try (InputStream stream = context.getEntityStream()) {
stream.transferTo(body); // <-- This is line 36 from the provided stack-trace
}
String algorithm = digest.split("=", 2)[0];
try {
String calculated = generator.generate(algorithm, body.toByteArray());
if (digest.equals(calculated)) {
context.setEntityStream(new ByteArrayInputStream(body.toByteArray()));
} else {
throw new InvalidDigestException("Calculated digest does not match supplied digest. Request body may have been tampered with.");
}
} catch (NoSuchAlgorithmException e) {
throw new InvalidDigestException(String.format("Unsupported hash algorithm: %s", algorithm), e);
}
}
}
}
The above filter is made available to services as a java-lib. We also supply a set of RequestFilters that can be used with various Http clients, i.e., okhttp3, apache-httpclient, etc. These clients only generate digests when the body is "repeatable", i.e., not streaming.
The Issue:
In Jersey services and Spring Boot services, we do not run into issues. However, when we use Quarkus, we receive the following stack-trace:
2022-09-02 15:18:25 5.13.0 ERROR A blocking operation occurred on the IO thread. This likely means you need to use the #io.smallrye.common.annotation.Blocking annotation on the Resource method, class or javax.ws.rs.core.Application class.
2022-09-02 15:18:25 5.13.0 ERROR HTTP Request to /v1/policy/internal/policies/72575947-45ac-4358-bc40-b5c7ffbd3f35/target-resources failed, error id: c79aa557-c742-43d7-93d9-0e362b2dff79-1
org.jboss.resteasy.reactive.common.core.BlockingNotAllowedException: Attempting a blocking read on io thread
at org.jboss.resteasy.reactive.server.vertx.VertxInputStream$VertxBlockingInput.readBlocking(VertxInputStream.java:242)
at org.jboss.resteasy.reactive.server.vertx.VertxInputStream.readIntoBuffer(VertxInputStream.java:120)
at org.jboss.resteasy.reactive.server.vertx.VertxInputStream.read(VertxInputStream.java:82)
at java.base/java.io.InputStream.transferTo(InputStream.java:782)
at com.###.ciam.jaxrs.DigestValidationFilter.filter(DigestValidationFilter.java:36)
at org.jboss.resteasy.reactive.server.handlers.ResourceRequestFilterHandler.handle(ResourceRequestFilterHandler.java:47)
at org.jboss.resteasy.reactive.server.handlers.ResourceRequestFilterHandler.handle(ResourceRequestFilterHandler.java:8)
at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:141)
at org.jboss.resteasy.reactive.server.handlers.RestInitialHandler.beginProcessing(RestInitialHandler.java:49)
at org.jboss.resteasy.reactive.server.vertx.ResteasyReactiveVertxHandler.handle(ResteasyReactiveVertxHandler.java:17)
at org.jboss.resteasy.reactive.server.vertx.ResteasyReactiveVertxHandler.handle(ResteasyReactiveVertxHandler.java:7)
at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1212)
at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:163)
at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
at io.quarkus.vertx.http.runtime.StaticResourcesRecorder$2.handle(StaticResourcesRecorder.java:67) ... elided ...
I completely understand why Vert.x would like to prevent long-running I/O operations on the request processing threads. That said, the advice provided in the exception only accounts for I/O operations at the end of the request processing, i.e., it assumes the I/O is happening in the endpoint. Although we do control the filter code, it is in an external library, making it almost like a 3rd party library.
My Question:
What is the right way to handle this?
I've been scouring documentation, but haven't stumbled on the answer yet (or haven't recognized the answer). Is there a set of recommended docs I should review?
https://quarkus.io/guides/resteasy-reactive#request-or-response-filters
https://smallrye.io/smallrye-mutiny/1.7.0/guides/framework-integration/
#RequestScoped
class Filter(
private val vertx: Vertx
) {
// you can run blocking code on mutiny's Infrastructure defaultWorkerPool
#ServerRequestFilter
fun filter(requestContext: ContainerRequestContext): Uni<RestResponse<*>> {
return Uni.createFrom().item { work() }
.map<RestResponse<*>> { null }
.runSubscriptionOn(Infrastructure.getDefaultWorkerPool())
}
// or use vertx.executeBlocking api
#ServerRequestFilter
fun filter(requestContext: ContainerRequestContext): Uni<RestResponse<*>> {
return vertx.executeBlocking(
Uni.createFrom().item { work() }
.map { null }
)
}
private fun work(){
Log.info("filter")
Thread.sleep(3000)
}
}
In the end, the advice in the exception lead me to simply annotating a delegate ContainerRequestFilter:
public class DigestValidationFilterBlocking implements ContainerRequestFilter {
private final DigestValidationFilter delegate;
public DigestValidationFilterBlocking(DigestValidationFilter delegate) {
this.delegate = delegate;
}
#Blocking // <-- This annotation allowed Vert.x to accept the I/O operation
#Override
public void filter(ContainerRequestContext context) throws IOException {
delegate.filter(context);
}
}
I had the same problem. You can try using this in your #ServerRequestFilter:
#Context
HttpServerRequest request;
Retrofit 2 RequestBody writeTo() method called twice, the code which I used is given below:
ProgressRequestBody requestVideoFile = new ProgressRequestBody(videoFile, new ProgressRequestBody.UploadCallbacks() {
VideoUploadStore store = new VideoUploadStore();
#Override
public void onProgressUpdate(int percentage) {
if (!mIsCancelled) {
Log.i("UploadServiceManager", "Read Percentage : " + percentage);
data.setUploadPercentage(percentage);
store.updateUploadData(data);
}
}
#Override
public void onError() {
if(!mIsCancelled) {
data.setUploadPercentage(0);
store.updateUploadData(data);
}
}
#Override
public void onFinish() {
}
});
MultipartBody.Part multipartVideo = MultipartBody.Part.createFormData("File", videoFile.getName(), requestVideoFile);
The solution below might help you out , although it might be too late. :p
Remove HttpLoggingInterceptor Object in your Api Client which will not execute writeTo() function twice.Basically , HttpLoggingInterceptor loads the data buffer first ( for internal logging purpose ) by calling writeTo() and then again calls writeTo() for uploading the data to server.
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
httpClient.addInterceptor(logging);
Decreasing log level from BODY to HEADERS, BASIC or NONE solved this problem for me
I figured out yet another case for twice called writeTo() method.
I use OkHttpClient without Retrofit and HttpLoggingInterceptor, and I have the twice called problem.
Solution: the problem appears after upgrade Android Studio to 3.1.1 and enable Advanced Profiling in run project configuration. So disable Advanced Profiling.
If you're using writeTo() to track file upload progress, you need to distinguish between the callers of writeTo(). Basically writeTo() can be called by any interceptor in the chain, e.g. any logging interceptor such as a HttpLoggingInterceptor/OkHttpProfilerInterceptor/StethoInterceptor, and this method provides no context for that.
The simplest approach (as pointed out by other answers) is to get rid of those interceptors that require access to the request body. But this may be not always feasible.
Another solution is to make use of the fact that a server call is performed by a CallServerInterceptor which is the last interceptor in the chain (according to the docs). You can inspect stack trace prior to further handling. Yes, this is ugly. But this way you don't have to modify your interceptors or leave room for subtle bugs when someone else comes up and adds another interceptor.
override fun writeTo(sink: BufferedSink) {
val isCalledByCallServerInterceptor = Thread.currentThread().stackTrace.any { stackTraceElement ->
stackTraceElement.className == CallServerInterceptor::class.java.canonicalName
}
// TODO
}
decreasing level from BODY to HEADERS and removing HttpLoggingInterceptor is not a solution for me because it gave explanation about what is going on with the api called. you can just initiate counter variable like,
private int firstTimeCounter = 0; then
#Override
public void writeTo(BufferedSink sink) throws IOException {
firstTimeCounter += 1;
.......
.......
if(firstTimeCounter==2){
try {
while (total != file.length()) {
read = source.read(sink.buffer(), SEGMENT_SIZE);
total += read;
Log.e("progress ", total + " %");
sink.flush();
}
} finally {
okhttp3.internal.Util.closeQuietly(source);
}
}
}
Quite for a long time I'm wondering why doesn't EJBException use standard Throwable.cause field to reach an exception it wraps?
It complicates getting the original root cause to something like that
private String getRootCauseErrorMessage(final Exception ex) {
Throwable currentException = ex;
Throwable nextException = null;
do {
if (nextException != null) {
currentException = nextException;
}
/* For some reason EJBException stores cause in a separate field rather the all generic Throwables */
if (currentException instanceof EJBException) {
nextException = ((EJBException) currentException).getCausedByException();
} else {
nextException = currentException.getCause();
}
} while (nextException != null);
return currentException.getMessage();
}
ps: I'm on Java6 and EJB3
Throwable.getCause was not added until Java 1.4. Some implementations of EJBException do retrofit the getCausedByException method to use the getCause method (similar to how the RemoteException.getCause method was retrofitted), but it sounds like your application server does not do this.
in my application the follwoing code is used. Can some one give a detailed explanation for the code that is highlighted?
I understood that in first highlighted block java reflection is used in invoking the method handle_validation..but need the detailed explanation.
Then in second highlighted block RemoteException is thrown..
My exact question is why they used reflection to call EngineHandlerIF and then why they are using RMI in this EngineHandlerIF to invoke the definition of method in EngineHandler?
private static EngineHandlerIF init() {
ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[] { "validation.xml" });
String[] beans = ctx.getBeanDefinitionNames();
for (String string : beans) {
logger.info(string);
}
EngineHandlerIF engine = (EngineHandlerIF) ctx.getBean("engine");
return engine;
}
private Object callEngineMethod(MiddlewareMessage mmsg) {
Object resultObj;
try {
**String methodName = "handle_validation";
Method method = EngineHandlerIF.class.getDeclaredMethod(methodName, MiddlewareMessage.class);
method.setAccessible(true);
resultObj = method.invoke(engine, new Object[] { mmsg });**
} catch (Exception e) {
logger.error("sendMessage Exception: ", e);
return new Boolean(false);
}
return resultObj;
}
EngineHandlerIF:
----------------
**public abstract String handle_validation(MiddlewareMessage mmsg) throws RemoteException;**
EngineHandler:
--------------
public String handle_validation(MiddlewareMessage mmsg) throws Exception {
//some code
}
I understood that in first highlighted block java reflection is used
in invoking the method handle_validation..but need the detailed
explanation.
That's pretty much it. The only other bit is the
method.setAccessible(true);
which makes the method accessible to the caller (e.g. from private to public), thus allowing you to call it. However the above method does appear to be public already. Perhaps this is some legacy following a refactor ?
Note that this isn't RMI (remote method invocation), but rather reflection. The only RMI I can see here is the handle_validation() method possibly throwing a RemoteException.
Maybe someone had just discovered the hammer of a reflection so everything, including method that were already public, started looking like a nut.
It is garbage: throw it away. Just call the method directly.
Our application uses several back-end services and we maintain wrappers which contain the methods to make the actual service calls. If any exception occurs in any of those methods while invoking a service, we throw a custom exception encapsulating the original exception as shown below.
interface IServiceA {
public void submit(String user, String attributes);
}
public class ServiceAWrapper implements IserviceA {
private ActualService getActualService() {
.....
}
public void submit(String user, String attributes) {
try {
Request request = new Request();
request.setUser(user);
request.setAttributes(attributes);
getActualService().call(request);
} catch(ServiceException1 e) {
throw new MyException(e, reason1);
} catch(ServiceException2 e) {
throw new MyException(e, reason2);
}
}
}
I would like to know if there's any framework that would allow me to
capture (and probably log) all the
parameters passed to my wrapper
methods at run-time; if the methods
are called.
capture the actual exception
object(MyException instance in above
example), if any thrown; so that I
could append the passed parameters
to the object at run-time.
I am currently exploring AspectJ to see if it can address my requirements, but I am not sure if it can be used to capture the parameters passed to methods at runtime and also to capture exception objects, if any occur.
Thanks.
With AspectJ, you can use around advice to execute advice instead of the code at the join point. You can then execute the actual join-point from within the advice by calling proceed. This would allow you to capture the input parameters, log them, and proceed to call the actual method.
Within the same advice you could capture any logs throw from the method, and inspect or log them before passing it back up to higher levels.
Matt B's answer is right. Specifically, you can do something like this:
aspect MonitorServiceCalls {
private final Logger LOG = LoggerFactory.getLog("ServiceCallLog");
Object around() throws MyException: call(public * *(..) throws MyException)
&& target(IServiceA+) {
MethodSignature msig = (MethodSignature)thisJoinPoint;
String fullMethName = msig.getMethod().toString();
try {
Object result = proceed();
LOG.info("Successful call to {} with arguments {}",
fullMethName,
thisJoinPoint.getArgs());
return result;
} catch(MyException e) {
LOG.warn("MyException thrown from {}: {}", msig.getMethod(), e);
throw e;
}
}
}
AspectJ is the right option. You will be able to get hold of the parameters by way of a JoinPoint object that will be passed to your advise methods. You can also get hold of the exception either by implementing an after throwing advise or an around advise.