CXF Custom Validation Interceptor with custom response not working - java

I am trying to write a Custom CXF Interceptor to do some validations on SOAP request to a web service. Based on the validation results, I want to block the request to web service and return the response with some modified parameters.
For this, I have written custom CXF ininterceptor extending from AbstractPhaseInterceptor, to run in phase USER_LOGICAL, which does validations, but I am not able to stop the subsequent call to web service and also not able to pass the Custom Response object(Custom Response object type is same as web service return type). How can I do this using interceptors?

I did some research upon the tip from nadirsaghar and I found it to be the cleanes solution available. Using message.getExchange() in JAX-WS is a complete pain, since you have to setup a conduit and fill the response message yourself...
So better do it this way, using HttpServletResponse. - You need to have the java servlet-api.jar on your Path. If you're developing without maven, just link it from your webserver (e.g. tomcat) directory, but exlude it from deployment.
<!-- With Maven add the following dependency -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<!-- The version should match your WebService version e.g. 3.0 for JDK7-->
<version>2.5</version>
<scope>provided</scope>
</dependency>
With scope provided it will not be deployed and is just available so you can access the HttpServletResponse class.
Your Handler Code:
#Override
public void handleMessage( final Message message ) throws Fault
{
if( shouldBlockMessage( message ) )
{
message.getInterceptorChain().abort();
final HttpServletResponse response = (HttpServletResponse)message.get( AbstractHTTPDestination.HTTP_RESPONSE );
// To redirect a user to a different Page
response.setStatus( HttpServletResponse.SC_MOVED_TEMPORARILY );
response.setHeader( "Location", "http://www.bla.blubb/redirectPage" );
// Other possibility if a User provides faulty login data
response.setStatus( HttpServletResponse.SC_FORBIDDEN );
}
}

You can abort execution of the interceptorChain including the webservice using abort method
public void handleMessage(SoapMessage message) {
InterceptorChain chain = message.getInterceptorChain();
chain.abort();
}

Something like this, there is no need to play with Interceptor chain.
public void handleMessage(Message message) {
//your logic
Response response = Response.status(Status.UNAUTHORIZED).type(MediaType.APPLICATION_JSON).build();
message.getExchange().put(Response.class, response);
}

Related

How to add dynamic header values to feign-client through Feign interceptor from current request?

I am new to Spring-cloud-openfeign. I am trying to build a micro-service based system. To make things look simpler, just take 3 of those services, which are Gateway, Service-A, Service-B. When I make a request from my frontend to Service-A through Gateway, the gateway will verify the JWT token in the request, and extract user information (userId) from the token and will put in the request as a header and will forward it to Service-A. Now Service-A will call service-B through feign-client. Now during this inter-service call through feign-client, I am trying to forward userId from the current request in Serice-A to outgoing request to service-B through a Feign RequestIterceptor. But I am not able to retrieve current request in the interceptor. I tried the solution in this stack_over_question, but it doesn't seem to work. I think I face the same problem as of this github_issue. I could see some blogs and articles advising to use RequestContextListner or RequestContextFilter to get the current request out of the Dispatcher-Servlet, But I couldn't find out how to use or implement it. Following is the code I am currently using
#Component
public class ProxyInterceptor implements RequestInterceptor {
private final static Logger LOGGER = LoggerFactory.getLogger(ProxyInterceptor.class);
#Override
public void apply(RequestTemplate requestTemplate) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
String userId = request.getHeader("userId");
LOGGER.info("userId {}", userId);
if (Optional.ofNullable(userId).isPresent()) {
requestTemplate.header(USER_ID_REQUEST_HEADER_VARIABLE, userId);
}
}
}
Dependecies
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
<spring-cloud.version>Hoxton.SR8</spring-cloud.version>
This code throws a NullPointer exception as RequestContextHolder.getRequestAttributes() return null. Any idea how to solve this problem?
I had similar use case where I need to get header in the interceptor. The code is similar to below.
circuitBreakerFactory
.create("createUserProfile").run(
() -> {
String resp = callerClient.callExternalService();
return resp;
}, exception -> {
logger.info(exception.getMessage());
}
);
The issue was that the first parameter in the run method is a lambda function and therefore creates a new thread. This new thread does not have request context information of the previous thread that handled the request.
Add the following bean to the configuration and then the new thread will have request context inherited. Therefore the RequestContextHolder.getRequestAttributes() will not return null.
#Bean
DispatcherServlet dispatcherServlet() {
DispatcherServlet servlet = new DispatcherServlet();
servlet.setThreadContextInheritable(true);
return servlet;
}
You can refer to this answer which helped me a lot.

How to handle a POST request with a Servlet API

I am trying to make an API with Jetty Server, and I have this simple GET request:
#GET
public String helloWorld(){
return "Hello world";
}
In order to make a POST request, I assume that one must save the input to the Jetty server. I have tried to research for quite a while, but found nothing.
I imagine something like this:
#POST
public void Save(String stringToSave) {
// Save to DB?
}
You could likely google this but let me give you a quick overview. A Servlet is a chunk of code that is normally run during an HTTP action - GET, POST, etc. It is the original technology of the JavaEE world, having been released in the late 1990's.
A simple Java servlet, using modern annotations, would look something like:
#WebServlet(name = "SampleServlet", urlPatterns = "/sampleServlet")
public class SampleServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// called when an HTTP POST is sent
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// called when an HTTP GET is sent
}
}
The important parts to note are that the class extends HttpServlet and that you have to write code to pull data out of the request and push it into the response. This isn't bad to do but it does have to be done.
JAX-RS is a newer standard, aimed simplifying the creation of REST services. It too is a chunk of code that runs during an HTTP interaction.
A simple example of this would be:
#Path("/sampleService")
public class SampleService{
#Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
#POST
#Path("/v1/hello")
public Response sayHello( SomeObject someobject ) {
The code here is both simpler and a bit more complex. The use of annotations helps determine the path that the service exists on a URL (in this case /sampleService/v1/hello), the HTTP method, and the Content-Type for both the request and response. Additionally, if the SomeObject object is defined correctly, the JAX-RS framework will automatically deserialize the incoming JSON or XML payload into an object for you.
The Response object contains the HTTP response code (perhaps a teapot) and a response body. In this example, the body will be automatically serialized back to the requestor in a way that matches the Accept header of the HTTP request (i.e., JSON for an application/json Accept header and XML for application/xml).
Note that while not directly related the JAX-RS framework takes advantage of the Servlet framework. Indeed in JAX-RS you can access the HttpServletRequest and HttpServletResponse object in your methods.
Which way is "better"? In general I would recommend using JAX-RS where possible as it is the newer standard and is a bit easier to implement. However, if you do any work in the JavaEE world you're very likely to run into Servlet code so it's important to understand it too.
Note that both Servlets and JAX-RS require an application server of some sort. Jetty is one of those. Another very common one is Tomcat. The application server sets up the environment for your code and listens for incoming HTTP messages. When it gets one it looks to see if it knows how to handle the URL and routes to the appropriate place. In the servlet world the server routes solely on the URL. In the JAX-RS world the server routes on the URL and, if specified by the #Consumes annotation, the HTTP Content-Type header too.
There is much more but let's start there and see if it answers what you're after.

Unable to test Jax-rs with JSON entity

I am trying to test a Jax-rs resource by following this https://jersey.java.net/documentation/latest/test-framework.html,
and I am using container jersey-test-framework-provider-jdk-http
I can assert status code. However, when I try to readEntity, I get exception:
javax.ws.rs.ProcessingException: Unable to find a MessageBodyReader of content-type application/json and type class java.lang.String
at org.jboss.resteasy.core.interception.ClientReaderInterceptorContext.throwReaderNotFound(ClientReaderInterceptorContext.java:39)
at org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.getReader(AbstractReaderInterceptorContext.java:73)
at org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.proceed(AbstractReaderInterceptorContext.java:50)
at org.jboss.resteasy.client.jaxrs.internal.ClientResponse.readFrom(ClientResponse.java:248)
at org.jboss.resteasy.client.jaxrs.internal.ClientResponse.readEntity(ClientResponse.java:181)
at org.jboss.resteasy.specimpl.BuiltResponse.readEntity(BuiltResponse.java:217)
My Resource Class:
#Path("/")
public class SampleResource {
#GET
#Path("/health")
#Produces(MediaType.APPLICATION_JSON)
public String getServiceStatus() {
return "{\"Status\": \"OK\"}";
}
}
My Test Class:
public class TestSampleResource extends JerseyTest {
#Override
protected Application configure() {
return new ResourceConfig(SampleResource.class);
}
#Test
public void testHealthEndpoint() {
Response healthResponse = target("health").request(MediaType.APPLICATION_JSON).get();
Assert.assertEquals(200, healthResponse.getstatus()); // works
String body = healthResponse.readEntity(String.class);
Assert.assertEquals("{\"Status\": \"OK\"}", body);
}
}
Can anyone please help?
The problem comes from having both Jersey and RestEasy client on the classpath. When you call target() on the JerseyTest, the WebTarget is obtained from a Client that is built by calling ClientBuilder.newClient().
The ClientBuilder is a standard JAX-RS API, and it is implemented first to search for an implementation of ClientBuilder through the META-INF/services files, looking for a file named javax.ws.rs.client.ClientBuilder, whose content is the name of an implementation of the ClientBuilder. If no such file is found, it defaults to looking for JerseyClientBuilder.
jersey-client has no such file META-INF/services/javax.ws.rs.core.ClientBuilder because it's ClientBuilder is the default for JAX-RS client. If you look in your resteasy-client jar, you will see the it does have that file. And if you look in the contents of that file, you will see the ResteasyClientBuilder as the implementation.
So even though you are using Jersey's test framework, the Client being used, is RESTeasy's implementation. And I guess all the standard configurations with entity providers never gets configured. Conversion between String and application/json is one of those standard providers you need in your case.
I would say just explicitly use Jersey client implementation. You will no longer be able to call target on the JerseyTest. You will need to explicitly create the client
#Test
public void dotest() {
final Client client = new JerseyClientBuilder().build();
WebTarget target = client.target("http://localhost:9998");
final Response response = target.path("health").request().get();
final String json = response.readEntity(String.class);
}
The default base path for JerseyTest is http://localhost:9998, so I explicitly create the WebTarget with that.
Note that I said the String to/from application/json is supported by standard providers. So if you will only be serializing Strings, then you don't need anything else. If you want POJO serialization support for the Jersey client (and server side), you should add the following
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>${jersey2.version}</version>
<scope>test</scope>
</dependency>
I suspect the json parser in your test is being misguided by the presence of curly braces. Basically it thinks you are returning a json object, not a json string. Try returning "Status:OK"
As the exception says you are missing a MessageBodyReader for content-type application/json. Do you have JacksonJsonProvider on your classpath? It can be added as a dependency to jackson-jaxrs-json-provider:
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.7.3</version>
</dependency>
Then register the JacksonJsonProvider in your test application:
#Override
protected Application configure() {
return new ResourceConfig(SampleResource.class, JacksonJsonProvider.class);
}

Configure Response object for Rest Services inside a Jersey-Grizzly server, in OSGi container (CORS error prevention with Jersey 1x)

The last couple of days, I have been struggling with an issue. I've created a rest service hosted by a Grizzly server inside an OSGi container. Everything is working perfectly at this point.
Now, I want to add a header in every response.Not so complex or illogical right? Yet, I can't find a way to do it.
I have tried to:
1) Get the response object inside the rest functions as this question suggests (pretty textbook when you are not under OSGi).
2) Add a handler using the code above (in this case the service method is never called)
server.getServerConfiguration().addHttpHandler(
new HttpHandler() {
#Override
public void service(Request arg0, Response arg1)
throws Exception {
arg1.setHeader("Access-Control-Allow-Origin", "*");
}
});
I am using jersey-server/client/core 1.18.1 and grizzly2-server 1.18.1, hence i prefer a solution that can be applied in this version, but I am willing to update jar versions if it cannot be done in 1.18.x.
You could give a try to Jersey filters.
In a nutshell, you should create class implementing ContainerResponseFilter:
public class MyFilter implements ContainerResponseFilter {
#Override
public void filter(
ContainerRequest request,
ContainerResponse response
) throws IOException {
request.getHttpHeaders().add(<header name>, <header value>);
}
}
Then, you should register this filter in your Jersey server configuration.
Please, note, that this filter would be invoked on every response. To bind it only to specific resources, you could use annotation-binding, that is described here.
All other information you could find here.

Calling SOAP web service from GWT client

I don't have any experience working with SOAP so please help me.
i have web project with GWT on the client side. as for the information needed for this site, i have to call/access SOAP web service. i've read some forums that i should use RequestBuilder in order to do so. i have the following code:
RequestBuilder builder = new RequestBuilder( RequestBuilder.POST, URL.encode( url ) );
try {
builder.sendRequest( null, new RequestCallback() {
public void onResponseReceived( Request request, Response response) {
if (200 == response.getStatusCode()) {
// processing response here
} else {
// Handle the error
}
}
public void onError(Request request, Throwable exception) {
// error
}
});
} catch (RequestException e) {
// Couldn't connect to server
}
This code causes an error and return status code 0. I think it's the SOP(Same-Origin-Policy) again.
Is there any other way I can do to access SOAP web service in GWT?
E D I T
In this project, a .wsdl file, which is located from an existing domain,
http://sample.com/server/soap/soap.wsdl
is already provided. And I also have this:
http://sample.com/server/soap/soapserver.php
How does it help me to connect to the SOAP web service?
I have created a SOAP Client in java but i encountered an error on javax.xml.* about inheriting the required modules.
If you're trying to access SOAP service from another domain then you're probably limited by SOP. I'd suggest building a thin server-side layer that will actually talk to the SOAP service. To talk to your GWT server-side you can use, for example, DispatchAsync or RequestFactory.

Categories

Resources