I’m user of CXF in an SOA development context.
I’m wondering if my problem has a solution with CXF. Here’s my need.
We have developed a webapp serving JAXWS endpoints, the endpoint implementations consists in analyzing the request via interceptors, storing data from the request to a database from the service layer in Java, and resend the original request to another server via a CXF client.
The point is that some of our requests contains a DSIG signature (https://www.w3.org/TR/xmldsig-core/) or an signed SAML Assertion.
Our need is to resend the requests without altering them (such as a proxy) from a CXFClient. CXF uses to send the marshalled object to the server, but this way the original stream is not sent
Is there a way to resend the incoming request from the service layer from a Java CXFClient without altering it (the Signatures depends on the format of the request: blanks, namespaces prefixes, carriage returns…) ? We prefer the CXFClient because we would like to reuse our homemade CXF interceptor which logs the outgoing request.
We have tested an interceptor to intend to replace the outputStream by the original request before sending it to the server, we used this answer: How To Modify The Raw XML message of an Outbound CXF Request?, but we are still KO, CXF still sends the stream made from the marshalled object. See code below.
Context:
- CXF 2.7.18 (JDK 6), and 3.1.10 (JDK 8)
- Platform: windows 7 64bit/ rhel 7 64bit
- Apache Tomcat 7
- Tcpdump to analyse incoming traffic
Sample of code of our client:
final Client cxfClient = org.apache.cxf.frontend.ClientProxy.getClient( portType );
cxfClient.getInInterceptors().clear();
cxfClient.getOutInterceptors().clear();
cxfClient.getOutFaultInterceptors().clear();
cxfClient.getRequestContext().put(CustomStreamerInterceptor.STREAM_TO_SEND,
PhaseInterceptorChain.getCurrentMessage().getContent( InputStream.class ) );
cxfClient.getOutInterceptors().add( new CustomStreamerInterceptor() );
org.apache.cxf.transport.http.HTTPConduit http = (org.apache.cxf.transport.http.HTTPConduit) cxfClient.getConduit();
...
port.doSomething(someRequest);
CustomStreamerInterceptor:
package test;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.commons.io.IOUtils;
import org.apache.cxf.binding.soap.interceptor.SoapOutInterceptor.SoapOutEndingInterceptor;
import org.apache.cxf.helpers.LoadingByteArrayOutputStream;
import org.apache.cxf.interceptor.AbstractOutDatabindingInterceptor;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.io.CacheAndWriteOutputStream;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.Phase;
public class CustomStreamerInterceptor extends AbstractOutDatabindingInterceptor {
public static final String STREAM_TO_SEND = "STREAM_TO_SEND";
public CustomStreamerInterceptor () {
super( Phase.WRITE_ENDING );
addAfter( SoapOutEndingInterceptor.class.getName() );
}
#Override
public void handleMessage( Message message ) throws Fault {
try {
InputStream toSend = (InputStream) message.get( STREAM_TO_SEND );
if ( toSend != null ) {
toSend.reset();
LoadingByteArrayOutputStream lBos = new LoadingByteArrayOutputStream();
IOUtils.copy( toSend, lBos );
CacheAndWriteOutputStream cawos = (CacheAndWriteOutputStream) message.getContent( OutputStream.class );
cawos.resetOut( lBos, false );//fail !
}
}
catch ( Exception e ) {
throw new Fault( e );
}
}
}
Thanks you for any help, it would be very usefull.
I think it's better to create a "classic" HTTP client because CXF is not designed for that kind of situation, it's more common to use it to marshal objects from java to XML...
Fortunately for you I deal with this problem with an interceptor. You can write an interceptor that copy the stream in the output stream object that CXF is preparing to send to the server. You need to be carefull to the phase and the order of you interceptors because if you use a Logging interceptor you may want to log the outgoing stream. This interceptor may do the job, be sure it runs after any logging interceptor. Code for CXF 2.7.18 :
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.commons.lang.CharEncoding;
import org.apache.cxf.helpers.IOUtils;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.interceptor.StaxOutInterceptor;
import org.apache.cxf.message.Exchange;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PassePlatClientInterceptorOut extends AbstractPhaseInterceptor<Message> {
private static final Logger LOG = LoggerFactory.getLogger( PassePlatClientInterceptorOut.class );
private final Exchange exchangeToReadFrom;
public PassePlatClientInterceptorOut( final Exchange exchange ) {
super( Phase.PRE_STREAM );
addBefore( StaxOutInterceptor.class.getName() );
this.exchangeToReadFrom = exchange;
}
#Override
public void handleMessage( Message message ) {
InputStream is = (InputStream) exchangeToReadFrom.get( PassePlatServerInterceptorIn.PASSE_PLAT_INTERCEPTOR_STREAM_SERVEUR );
if ( is != null ) {
message.put( org.apache.cxf.message.Message.ENCODING, CharEncoding.UTF_8 );
OutputStream os = message.getContent( OutputStream.class );
try {
IOUtils.copy( is, os );
is.close();
}
catch ( IOException e ) {
LOG.error( "Error ...", e );
message.setContent( Exception.class, e );
throw new Fault( new Exception( "Error ...", e ) );
}
boolean everythingOK = message.getInterceptorChain().doInterceptStartingAt( message,
org.apache.cxf.interceptor.MessageSenderInterceptor.MessageSenderEndingInterceptor.class.getName() );
if ( !everythingOK ) {
LOG.error( "Error ?" );
throw new Fault( new Exception( "Error ..." ) );
}
}
}
}
To create the interceptor:
cxfClient.getInInterceptors().add( new PassePlatClientInterceptorIn( exchange ) );
Related
On the server side, I develop a REST API with Java and Jersey / Jackson, and this API makes calls to the Stripe API.
The Stripe API returns all objects with properties names in snake case, such as client_secret for class PaymentIntent.
The JSON returned by my REST API using Jersey automatically converts these properties to camel case, with names such as clientSecret.
On the client side, I use the Stripe JS library, which also expects properties names in snake case, and therefore I get errors when I try to read properties of objects returned by my REST API.
I have seen many posts about configuring Jersey to use snake case instead of camel case, but I have not been able to apply what I found to my use case, which is using camel case for my own classes, and snake case for Stripe classes I have no control on.
Here is my current code on the server site:
package com.knowledgeplaces.metalmsapi.resources;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import com.knowledgeplaces.metalmsapi.records.PaymentIntentArgsRec;
import com.knowledgeplaces.metalmsapi.records.PaymentIntentResponseRec;
import com.knowledgeplaces.metalmsapi.utils.MetaLmsConstants;
import com.stripe.Stripe;
import com.stripe.model.PaymentIntent;
import com.stripe.exception.StripeException;
import com.stripe.net.ApiResource;
#Path("/eShops/{eShopId}/stripePaymentIntent")
public class StripePaymentIntentRest {
// request one Payment Intent
#POST
#Produces({ MediaType.APPLICATION_JSON })
public PaymentIntentResponseRec createService(
#Context HttpServletRequest req,
#PathParam("eShopId") Integer eShopId,
PaymentIntentArgsRec paymentIntentArgs) {
PaymentIntent paymentIntent;
PaymentIntentResponseRec paymentIntentResponse;
// get Stripe API secret key
if (paymentIntentArgs.stripeTestMode()) {
Stripe.apiKey = "***********************";
} else {
Stripe.apiKey = "***********************";
}
// create Payment Intent
List<Object> paymentMethodTypes = new ArrayList<>();
paymentMethodTypes.add("card");
Map<String, Object> params = new HashMap<>();
params.put("amount", paymentIntentArgs.amount());
params.put("currency", paymentIntentArgs.currency());
params.put(
"payment_method_types",
paymentMethodTypes);
try {
// paymentIntent = PaymentIntent.create(params);
paymentIntent = ApiResource.GSON.fromJson(PaymentIntent.create(params).toJson(), PaymentIntent.class);
paymentIntentResponse = new PaymentIntentResponseRec(null, paymentIntent);
} catch (StripeException ex) {
paymentIntentResponse = new PaymentIntentResponseRec(MetaLmsConstants.StripeApiError, null);
}
return paymentIntentResponse;
}
}
With this code, I get a Payment Intent object with properties in camel case.
If I uncomment the line
paymentIntent = PaymentIntent.create(params);
And comment the line
paymentIntent = ApiResource.GSON.fromJson(PaymentIntent.create(params).toJson(), PaymentIntent.class);
Then I get the following error:
No serializer found for class com.stripe.net.StripeResponse and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: com.knowledgeplaces.metalmsapi.records.PaymentIntentResponseRec["paymentIntent"]->com.stripe.model.PaymentIntent["lastResponse"])
Please advise on how to get rid off this error.
I have reviewed my code and here is a working solution:
try {
paymentIntent = PaymentIntent.create(params);
String paymentIntentJson = paymentIntent.toJson();
stringResponse = new StringResponseRec(null, paymentIntentJson);
} catch (StripeException ex) {
stringResponse = new StringResponseRec(ex.getMessage(), null);
}
return stringResponse;
The problem was related to snake case support in Jersey Jackson or records in Java 16, I don't know.
So, instead of loading a record of type PaymentIntentResponseRec which has a Stripe PaymentIntent object as a property, I load a record of type StringResponseRec which has a property of type string, and I load that string from the paymentIntent.toJson() provided by the Stripe API.
On the client side, my Angular app which uses the Stripe JS library gets my PaymentIntent object fine.
It works, but if you think there is a more elegant solution, feel free to comment.
I have created a java application to send/receive the topic messages using Azure Service Bus java SDK and it is working perfectly fine if I run it as a Java Application.
I exported the application as jar along with all the dependency jars to the {ColdFusionInstallation}\cfusion\lib and restarted ColdFusion Application Server.
I am able to create the class objects and see their definitions while dumping but when I trying to call any particular method from the class, it's taking forever.
<cfset objSender = createObject( "java", "com.test.Sender" ).init()>
<cfset objSender.sendAsync( JavaCast( "string", "cftest" ) )>
Sender.java
package com.test;
import java.util.concurrent.CompletableFuture;
import org.apache.log4j.Logger;
import com.microsoft.azure.servicebus.IMessage;
import com.microsoft.azure.servicebus.ITopicClient;
import com.microsoft.azure.servicebus.Message;
import com.microsoft.azure.servicebus.TopicClient;
import com.microsoft.azure.servicebus.primitives.ConnectionStringBuilder;
import com.microsoft.azure.servicebus.primitives.ServiceBusException;
public class Sender {
private final String namespaceConnectionString = "Endpoint=sb.......";
private final String namespaceTopicName = "test";
private static Logger logger = Logger.getRootLogger();
public CompletableFuture<Void> sendAsync( String message ) {
// Get client
ITopicClient topicClient = this.createTopicClient( this.namespaceTopicName );
// Send Async
CompletableFuture<Void> future = topicClient.sendAsync(
new Message( message )
).thenRunAsync( () -> {
try {
topicClient.close();
} catch (Exception e) {
logger.info( "Unable to close topic client!" );
}
} );
return future;
}
private ITopicClient createTopicClient( String topicName ) throws RuntimeException {
// Create client for topic
ITopicClient topicClient = null;
try {
// Create
topicClient = new TopicClient(
new ConnectionStringBuilder( this.namespaceConnectionString, topicName )
);
} catch ( InterruptedException | ServiceBusException e ) {
logger.info( "Unable to client for topic: " + topicName );
throw new RuntimeException( "Unable to create client for topic: " + topicName );
}
return topicClient;
}
}
I am not sure what is wrong as it is working when running directly.
Any suggestions greatly appreciated!
I'm trying to figure out how to debug my tests by viewing the HTTP my client is sending
testPutActivitity(com.lm.controller.JaxRSActivatorTest) Time elapsed: 0.521 sec <<< FAILURE!
java.lang.AssertionError: expected:<404> but was:<200>
at org.junit.Assert.fail(Assert.java:88)
at org.junit.Assert.failNotEquals(Assert.java:743)
at org.junit.Assert.assertEquals(Assert.java:118)
at org.junit.Assert.assertEquals(Assert.java:555)
at org.junit.Assert.assertEquals(Assert.java:542)
at com.lm.controller.JaxRSActivatorTest.testPutActivitity(JaxRSActivatorTest.java:44)
With the new undertow server, some things seem to have changed, so the documentation I'm finding no longer seems to be relevant.
What I'd like to know is how I can view the HTTP Request/Response transmitted from the client? (if it matters I should be using the RESTEasy client)
package com.lm.controller;
import com.lm.test.RESTCtrlTestBase;
import static com.lm.test.TestUtil.testWar;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.shrinkwrap.api.Filters;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class RootTest extends RESTCtrlTestBase {
public RootTest() {
}
Client client = ClientBuilder.newClient();
#Deployment( testable = false )
public static WebArchive createDeployment() {
WebArchive ar = testWar()
.addPackages(
false,
Filters.exclude( ".*Test.*" ),
"com.lm.controller",
"com.lm.activity"
);
// System.out.println( "ar = " + ar.toString( true ) );
return ar;
}
#Test
public void getRoot() {
WebTarget target = client.target( deploymentURI );
log.debug( "targetURI = {}", target.getUri() );
Response res = target.request().get();
assertEquals( 200, res.getStatus() );
}
}
I've tried writing an interceptor but it doesn't output the request/response exactly as it is transmitted, also it only seems to get loaded for the server but not the test client above.
I'm having a conceptual problem preventing me from solving a trivial problem. I need to send an object to a web service. I have an endpoint, and I have code that can serialize the object, so I can create an org.jdom.Document or a byte[] object containing the serialized object.
I can also create a client snippet that uses axis2 to invoke the web service.
Finally I have tried sending a manually created message to the web service (it has no WSDL ;( )
AND I have used Charles to see what is going out (the request).
What I don't know how to do is convert the byte[] or org.jdom.Document object to an OMElement object. Evidently the serviceClient.sendReceive(elem) takes an OMElement parameter.
Here is what I tried so far (I removed the OMElement that I sent out once I was convinced it was going out):
package testAxis2Client01;
import java.util.Map;
import javax.xml.stream.XMLStreamException;
import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.axiom.om.OMNamespace;
import org.apache.axiom.soap.SOAPEnvelope;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.client.Options;
import org.apache.axis2.client.ServiceClient;
import org.apache.axis2.transport.http.HTTPConstants;
public class testAxis2Client01 {
private static final int MXMOCONNECTIONTIMEOUT = 2;//don't really know what this should be.
/**
* #param args
*/
public static void main(String[] args) {
try {
callAxisWS();
} catch (XMLStreamException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void callAxisWS() throws XMLStreamException, Exception {
//Axis2 client code to call a WS
OMElement response=null;
try{
OMFactory factory = OMAbstractFactory.getSOAP11Factory();
SOAPEnvelope theEnvelope = OMAbstractFactory.getSOAP12Factory().getDefaultEnvelope();
theEnvelope.declareNamespace("http://www.w3.org/2001/XMLSchema","xsd");
theEnvelope.declareNamespace("http://www.w3.org/2001/XMLSchema-instance","xsi");
ServiceClient serviceClient = new ServiceClient();
Options options = serviceClient.getOptions();
options.setProperty(HTTPConstants.AUTO_RELEASE_CONNECTION, true); // Another API to release connection.
options.setTimeOutInMilliSeconds(10000); // Setting the connection timeout.
EndpointReference targetEPR = new EndpointReference(theUrl);
options.setTo(targetEPR);
options.setAction("processDocument");
serviceClient.setOptions(options);
//response = serviceClient.sendReceive(myOMElement);
response = serviceClient.sendReceive(elem)
if (response != null) {
System.out.println("SUCCESS!!");
System.out.println(response.toStringWithConsume());
}
}catch(Exception af){
af.printStackTrace();
System.out.println(af.getMessage());
}
}
}
The point of using axis2 is that it takes care of everything. You only have to provide a wsdl file and it will generate client stubs.
If you do not have an original wsdl, you can still make one yourself.
The best way for you is to create the wsdl file manually, generate the client stub and call the stub directly.
Is there an object within the standard Java SE that can accept a HTTP request from a socket? I have found how to create and send one, however I have not found a way to retrieve a HTTP object from a socket. I can create one my self, but I would rather rely on a heavily tested object.
This seems like something that would be readily available given the structure of JSP.
There is a small HTTP server in the Java 6 SDK (not sure if it will be in the JRE or in non-Sun JVM's).
From http://www.java2s.com/Code/Java/JDK-6/LightweightHTTPServer.htm :
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executors;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
public class HttpServerDemo {
public static void main(String[] args) throws IOException {
InetSocketAddress addr = new InetSocketAddress(8080);
HttpServer server = HttpServer.create(addr, 0);
server.createContext("/", new MyHandler());
server.setExecutor(Executors.newCachedThreadPool());
server.start();
System.out.println("Server is listening on port 8080" );
}
}
class MyHandler implements HttpHandler {
public void handle(HttpExchange exchange) throws IOException {
String requestMethod = exchange.getRequestMethod();
if (requestMethod.equalsIgnoreCase("GET")) {
Headers responseHeaders = exchange.getResponseHeaders();
responseHeaders.set("Content-Type", "text/plain");
exchange.sendResponseHeaders(200, 0);
OutputStream responseBody = exchange.getResponseBody();
Headers requestHeaders = exchange.getRequestHeaders();
Set<String> keySet = requestHeaders.keySet();
Iterator<String> iter = keySet.iterator();
while (iter.hasNext()) {
String key = iter.next();
List values = requestHeaders.get(key);
String s = key + " = " + values.toString() + "\n";
responseBody.write(s.getBytes());
}
responseBody.close();
}
}
}
Yeah, you make a new HTTP Request object from what you accept on the socket. What you do after that is up to you, but it should probably involve an HTTP Response.
import java.io.*;
import java.net.*;
import java.util.*;
public final class WebServer {
public static void main(String args[]) throws Exception {
int PORT = 8080;
ServerSocket listenSocket = new ServerSocket(PORT);
while(true) {
HttpRequest request = new HttpRequest(listenSocket.accept());
Thread thread = new Thread(request);
thread.start();
}
}
}
From: http://www.devhood.com/tutorials/tutorial_details.aspx?tutorial_id=396
There's some more work to be done in the tutorial, but it does look nice.
It looks like you are looking for a Servlet. A servlet is an API that lets you receive and respond to an HTTP request.
Your servlet gets deployed in a container, which is basically the actual Web server that will take care of all the protocol complexities. (The most populare are Tomcat and Jetty)