How does CXF Bus and STSClient deal with SSL Certificate and Proxy? - cxf

I'm using CXF 3.1.5, How can I apply proxy settings and trust or ignore SSL certificate when sending out request?
I use CXF in the following two ways.
Using org.apache.cxf.bus to get WSDL definition from IdP or SP, bus.getExtension(WSDLManager.class).getDefinition().
Using org.apache.cxf.ws.security.trust.STSClient to request Security Token.stsClient.requestSecurityToken()
and I think I need to use code for configuration instead of configuration file as my callers send me those information about proxy and SSL certificates.
thanks a lot!
After further research, I found something.
to resolve the first problem, add the following code:
ResourceManager extension = bus.getExtension(ResourceManager.class);
extension.addResourceResolver(new ResourceResolver() {
#Override
public <T> T resolve(String resourceName, Class<T> resourceType) {
return null;
}
#Override
public InputStream getAsStream(String name) {
if (!name.startsWith("https")) {
return null;
}
org.apache.http.client.HttpClient httpClient = HttpUtils.createHttpClient(setting);
HttpGet httpGet = new HttpGet(name);
try {
HttpResponse httpResponse = httpClient.execute(httpGet);
return httpResponse.getEntity().getContent();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
});
then I can get the WSDL definition, but I still don't know how to fix the second problem, I'm trying to use HTTPConduit((HTTPConduit)stsClient.getClient().getConduit()), but when call stsClient.getClient(), cxf will try to load those XML Schema which will lead to the following exception:
org.apache.cxf.service.factory.ServiceConstructionException: Failed to create service.
at org.apache.cxf.wsdl11.WSDLServiceFactory.create(WSDLServiceFactory.java:170)
at org.apache.cxf.ws.security.trust.AbstractSTSClient.createClient(AbstractSTSClient.java:657)
at org.apache.cxf.ws.security.trust.AbstractSTSClient.getClient(AbstractSTSClient.java:480)
...
Caused by: org.apache.ws.commons.schema.XmlSchemaException: Unable to locate imported document at 'https://...&xsd=ws-trust-1.3.xsd', relative to 'https://...#types1'.
at org.apache.cxf.catalog.CatalogXmlSchemaURIResolver.resolveEntity(CatalogXmlSchemaURIResolver.java:76)
at org.apache.ws.commons.schema.SchemaBuilder.resolveXmlSchema(SchemaBuilder.java:684)
at org.apache.ws.commons.schema.SchemaBuilder.handleImport(SchemaBuilder.java:538)
at org.apache.ws.commons.schema.SchemaBuilder.handleSchemaElementChild(SchemaBuilder.java:1516)
at org.apache.ws.commons.schema.SchemaBuilder.handleXmlSchemaElement(SchemaBuilder.java:659)
at org.apache.ws.commons.schema.XmlSchemaCollection.read(XmlSchemaCollection.java:551)
at org.apache.cxf.common.xmlschema.SchemaCollection.read(SchemaCollection.java:129)
at org.apache.cxf.wsdl11.SchemaUtil.extractSchema(SchemaUtil.java:140)
at org.apache.cxf.wsdl11.SchemaUtil.getSchemas(SchemaUtil.java:73)
at org.apache.cxf.wsdl11.SchemaUtil.getSchemas(SchemaUtil.java:65)
at org.apache.cxf.wsdl11.SchemaUtil.getSchemas(SchemaUtil.java:60)
at org.apache.cxf.wsdl11.WSDLServiceBuilder.getSchemas(WSDLServiceBuilder.java:378)
at org.apache.cxf.wsdl11.WSDLServiceBuilder.buildServices(WSDLServiceBuilder.java:345)
at org.apache.cxf.wsdl11.WSDLServiceBuilder.buildServices(WSDLServiceBuilder.java:209)
at org.apache.cxf.wsdl11.WSDLServiceFactory.create(WSDLServiceFactory.java:162)
... 32 more

Found a solution:
implements HTTPConduitFactory and put it into bus.
bus.setExtension(new MyHTTPConduitFactory(setting), HTTPConduitFactory.class)
In the Factory class:
#Override
public HTTPConduit createConduit(HTTPTransportFactory f, Bus b, EndpointInfo localInfo,
EndpointReferenceType target) throws IOException {
return new MyHTTPConduit(settings, f, b, localInfo, target);
}
MyHTTPConduit extends URLConnectionHTTPConduit
To handle SSL certificates.
TLSClientParameters parameters = new TLSClientParameters();
parameters.setDisableCNCheck(settings.isTurnOffHostVerifier());
if (settings.isIgnoreServerCertificate()) {
parameters.setTrustManagers(new TrustManager[] { new TrustAllCertsTrustManager() });
} else {
TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
factory.init(settings.getTrustStore());
parameters.setTrustManagers(factory.getTrustManagers());
}
this.setTlsClientParameters(parameters);
TrustAllCertsTrustManager class
private class TrustAllCertsTrustManager implements X509TrustManager {
#Override
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
}
#Override
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
}
#Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
To handle proxy.
HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
httpClientPolicy.setProxyServer(proxy.getHostName());
httpClientPolicy.setProxyServerPort(proxy.getPort());
this.setClient(httpClientPolicy);

There are some examples here: http://cxf.apache.org/docs/client-http-transport-including-ssl-support.html

Related

Codename One WebSocket + Spring Boot

About the Codename One Websocket cn1lib, I saw your lesson on the Whatsapp clone, however the code you proposed is for a complete app. Could you provide a simpler self-enclosed example of use of Websocket cn1lib with client-side code (Codename One code to send and receive messages) and server-side code (Spring Boot Java 8 to receive and send messages)?
I’m particulary interested in a simple Spring Boot example that interacts with Codename One, as start point to better understand and learn websocket.
Thank you
It would look roughly like this on the server:
public class WebSocketServer extends TextWebSocketHandler {
private static final Object LOCK = new Object();
private Map<String, WebSocketSession> sessions = new HashMap<>();
#Override
protected void handleTextMessage(WebSocketSession session,
TextMessage message) throws Exception {
Gson gson = new Gson();
MyDTO parsed = gson.fromJson(message.getPayload(), MyDTO.class);
// ... do stuff with incoming message
synchronized(LOCK) {
if(!sessions.contains(parsed.getId()) {
sessions.put(parsed.getId(), session);
}
}
}
public boolean sendMessage(String destId, String json) {
Session s = null;
synchronized(LOCK) {
s = sessions.get(destId);
}
if (s != null && s.isOpen()) {
try {
s.sendMessage(new TextMessage(s));
return true;
} catch (IOException e) {
synchronized(LOCK) {
sessions.remove(destId);
}
}
}
return false;
}
}

SpringBoot AngularJS webSocket integration

I'm trying to create webSocket on springBoot application.
this is config class:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").setAllowedOrigins("*").withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/sub");
config.setApplicationDestinationPrefixes("/send");
}
#Override
public void configureWebSocketTransport(WebSocketTransportRegistration registry) {
}
#Override
public void configureClientInboundChannel(ChannelRegistration registration) {
}
#Override
public void configureClientOutboundChannel(ChannelRegistration registration) {
}
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
}
#Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
}
#Override
public boolean configureMessageConverters(List<MessageConverter> messageConverters) {
return true;
}
}
Now from AngularJS I'm trying to connect to websocket using SockJS and Stomp
var socket = new SockJS('/api/ws');
var stompClient = Stomp.over(socket);
stompClient.connect({}, function (frames) {
stompClient.subscribe('/subs/hello', function (data) {
console.log(data);
});
});
When I open console I get this message:
admin-components.js:112173 WebSocket connection to 'wss://proxy.beta.corp.payment21.com/api/ws/135/hwq2yv3q/websocket' failed: Error during WebSocket handshake: Unexpected response code: 502
After 30 seconds I get this:
VM333 sockjs.min.js:2 Uncaught Error: Incompatibile SockJS! Main site uses: "1.4.0", the iframe: "1.0.0".
at s (VM333 sockjs.min.js:2)
And after 30 more seconds:
And it is working...
When I go to network to see details in the frames it says
(Opcode -1)
What is the problem here? Is it the spring configuration or SockJS?
Based on the Spring documentation the supported client SockJS version is 1.0.x
On the browser side, applications can use the sockjs-client (version 1.0.x). It emulates the W3C WebSocket API and communicates with the server to select the best transport option, depending on the browser in which it runs.

How to browse messages from a queue using Apache Camel?

I need to browse messages from an active mq using Camel route without consuming the messages.
The messages in the JMS queue are to be read(only browsed and not consumed) and moved to a database while ensuring that the original queue remains intact.
public class CamelStarter {
private static CamelContext camelContext;
public static void main(String[] args) throws Exception {
camelContext = new DefaultCamelContext();
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnectionFactory.DEFAULT_BROKER_URL);
camelContext.addComponent("jms", JmsComponent.jmsComponent(connectionFactory));
camelContext.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
from("jms:queue:testQueue").to("browse:orderReceived") .to("jms:queue:testQueue1");
}
}
);
camelContext.start();
Thread.sleep(1000);
inspectReceivedOrders();
camelContext.stop();
}
public static void inspectReceivedOrders() {
BrowsableEndpoint browse = camelContext.getEndpoint("browse:orderReceived", BrowsableEndpoint.class);
List<Exchange> exchanges = browse.getExchanges();
System.out.println("Browsing queue: "+ browse.getEndpointUri() + " size: " + exchanges.size());
for (Exchange exchange : exchanges) {
String payload = exchange.getIn().getBody(String.class);
String msgId = exchange.getIn().getHeader("JMSMessageID", String.class);
System.out.println(msgId + "=" +payload);
}
As far as I know, not possible in Camel to read (without consuming !) JMS messages...
The only workaround I found (in a JEE app) was to define a startup EJB with a timer, holding a QueueBrowser, and delegating the msg processing to a Camel route:
#Singleton
#Startup
public class MyQueueBrowser {
private TimerService timerService;
#Resource(mappedName="java:/jms/queue/com.company.myqueue")
private Queue sourceQueue;
#Inject
#JMSConnectionFactory("java:/ConnectionFactory")
private JMSContext jmsContext;
#Inject
#Uri("direct:readMessage")
private ProducerTemplate camelEndpoint;
#PostConstruct
private void init() {
TimerConfig timerConfig = new TimerConfig(null, false);
ScheduleExpression se = new ScheduleExpression().hour("*").minute("*/"+frequencyInMin);
timerService.createCalendarTimer(se, timerConfig);
}
#Timeout
public void scheduledExecution(Timer timer) throws Exception {
QueueBrowser browser = null;
try {
browser = jmsContext.createBrowser(sourceQueue);
Enumeration<Message> msgs = browser.getEnumeration();
while ( msgs.hasMoreElements() ) {
Message jmsMsg = msgs.nextElement();
// + here: read body and/or properties of jmsMsg
camelEndpoint.sendBodyAndHeader(body, myHeaderName, myHeaderValue);
}
} catch (JMSRuntimeException jmsException) {
...
} finally {
browser.close();
}
}
}
Apache camel browse component is exactly designed for that. Check here for the documentation.
Can't say more since you have not provided any other information.
Let's asssume you have a route like this
from("activemq:somequeue).to("bean:someBean")
or
from("activemq:somequeue).process(exchange -> {})
All you got to do it put a browse endpoint in between like this
from("activemq:somequeue).to("browse:someHandler").to("bean:someBean")
Then write a class like this
#Component
public class BrowseQueue {
#Autowired
CamelContext camelContext;
public void inspect() {
BrowsableEndpoint browse = camelContext.getEndpoint("browse:someHandler", BrowsableEndpoint.class);
List<Exchange> exchanges = browse.getExchanges();
for (Exchange exchange : exchanges) {
......
}
}
}

Camel blueprint testing and cucumber

Is it possible to combine cucumber with CamelBlueprintTestSupport? I have my runner class:
#RunWith(Cucumber.class)
#CucumberOptions(monochrome=true,
format={ "pretty", "html:target/cucumber"},
features = "C:/Users/Developer/workspace_camel/SRV002_PatronInformation/src/test/resources/cucumber/asynchronousErrorHandling.feature")
public class RunFeature_SRV002_PatronInformationTest {
}
and my blueprint test class with the scenarios:
public class SRV002_PatronInformationScenarioTest extends CamelBlueprintTestSupport {
#Override
protected String getBlueprintDescriptor() {
return "/OSGI-INF/blueprint/blueprint.xml";
}
#Given("^client communicates asynchronous via socket$")
public void client_communicates_asynchronous_via_socket() throws Throwable {
System.out.println("test");
}
#When("^client posts message$")
public void an_error_occurs_inside_the_integration() throws Throwable {
String endpoint = "netty4:tcp://localhost:5000?sync=false&textline=true";
template.sendBody(endpoint, "test");
}
#Then("^the integration should not return response to the client$")
public void the_integration_should_not_return_the_error_to_the_client() throws Throwable {
System.out.println("test");
}
}
The problem now is that, when I run this I run into nullpointerexception at template.sendbody because the context, bundle and routes haven't started. For some reason it seems adding #RunWith(Cucumber) prevents the camel routes from starting.
Anyone knows how this can be solved? Thanks
Souciance
Ok so I managed to solve this.
For reference look here:
http://camel.465427.n5.nabble.com/How-to-test-routes-when-using-another-TestRunner-td5772687.html
Thanks to Gregor Lenz for the help.
Essentially the key here is that in your Camel BlueprintTestSupport class, inside the test method, that starts the given scenario you need to add this.setUp(). See the code below:
In Cucumber
SRVXXX_FileTransferCamelRunner filetransfer = new SRVXXX_FileTransferCamelRunner();
#Given("^an input file$")
public void an_input_file() throws Throwable {
endpoint.append("file:C:/Camel/input?fileName=input.txt");
}
#When("^client puts the file in the input directory$")
public void client_puts_the_file_in_the_input_directory() throws Throwable {
filetransfer.testPutFile(fileData.toString(), endpoint.toString());
}
#Then("^the integration should move the file to the output directory$")
public void the_integration_should_move_the_file_to_the_output_directory() throws Throwable {
String outputPath = "C:/Camel/output/input.txt";
filetransfer.testFileHasMoved(outputPath);
}
In Camel
#Test
public void testPutFile(String body, String endpoint) throws Exception {
this.setUp();
template.sendBody(endpoint,body);
Thread.sleep(2000);
assertFileNotExists(endpoint);
}

Accept Multipart file upload as camel restlet or cxfrs endpoint

I am looking to implement a route where reslet/cxfrs end point will accept file as multipart request and process. (Request may have some JSON data as well.
Thanks in advance.
Regards.
[EDIT]
Have tried following code. Also tried sending file using curl. I can see file related info in headers and debug output, but not able to retrieve attachment.
from("servlet:///hello").process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
Message in = exchange.getIn();
StringBuffer v = new StringBuffer();
HttpServletRequest request = (HttpServletRequest) in
.getHeaders().get(Exchange.HTTP_SERVLET_REQUEST);
DiskFileItemFactory diskFile = new DiskFileItemFactory();
FileItemFactory factory = diskFile;
ServletFileUpload upload = new ServletFileUpload(factory);
List items = upload.parseRequest(request);
.....
curl :
curl -vvv -i -X POST -H "Content-Type: multipart/form-data" -F "image=#/Users/navaltiger/1.jpg; type=image/jpg" http://:8080/JettySample/camel/hello
following code works (but can't use as it embeds jetty, and we would like to deploy it on tomcat/weblogic)
public void configure() throws Exception {
// getContext().getProperties().put("CamelJettyTempDir", "target");
getContext().setStreamCaching(true);
getContext().setTracing(true);
from("jetty:///test").process(new Processor() {
// from("servlet:///hello").process(new Processor() {
public void process(Exchange exchange) throws Exception {
String body = exchange.getIn().getBody(String.class);
HttpServletRequest request = exchange.getIn().getBody(
HttpServletRequest.class);
StringBuffer v = new StringBuffer();
// byte[] picture = (request.getParameter("image")).getBytes();
v.append("\n Printing All Request Parameters From HttpSerlvetRequest: \n+"+body +" \n\n");
Enumeration<String> requestParameters = request
.getParameterNames();
while (requestParameters.hasMoreElements()) {
String paramName = (String) requestParameters.nextElement();
v.append("\n Request Paramter Name: " + paramName
+ ", Value - " + request.getParameter(paramName));
}
I had a similar problem and managed to resolve inspired by the answer of brentos. The rest endpoint in my case is defined via xml:
<restContext id="UploaderServices" xmlns="http://camel.apache.org/schema/spring">
<rest path="/uploader">
<post bindingMode="off" uri="/upload" produces="application/json">
<to uri="bean:UploaderService?method=uploadData"/>
</post>
</rest>
</restContext>
I had to use "bindingMode=off" to disable xml/json unmarshalling because the HttpRequest body contains multipart data (json/text+file) and obviously the standard unmarshaling process was unable to process the request because it's expecting a string in the body and not a multipart payload.
The file and other parameters are sent from a front end that uses the file upload angular module: https://github.com/danialfarid/ng-file-upload
To solve CORS problems I had to add a CORSFilter filter in the web.xml like the one here:
public class CORSFilter implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException,
ServletException {
HttpServletResponse httpResp = (HttpServletResponse) resp;
HttpServletRequest httpReq = (HttpServletRequest) req;
httpResp.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, CONNECT, PATCH");
httpResp.setHeader("Access-Control-Allow-Origin", "*");
if (httpReq.getMethod().equalsIgnoreCase("OPTIONS")) {
httpResp.setHeader("Access-Control-Allow-Headers",
httpReq.getHeader("Access-Control-Request-Headers"));
}
chain.doFilter(req, resp);
}
#Override
public void init(FilterConfig arg0) throws ServletException {
}
#Override
public void destroy() {
}
}
Also, I had to modify a little bit the unmarshaling part:
public String uploadData(Message exchange) {
String contentType=(String) exchange.getIn().getHeader(Exchange.CONTENT_TYPE);
MediaType mediaType = MediaType.valueOf(contentType); //otherwise the boundary parameter is lost
InputRepresentation representation = new InputRepresentation(exchange
.getBody(InputStream.class), mediaType);
try {
List<FileItem> items = new RestletFileUpload(
new DiskFileItemFactory())
.parseRepresentation(representation);
for (FileItem item : items) {
if (!item.isFormField()) {
InputStream inputStream = item.getInputStream();
// Path destination = Paths.get("MyFile.jpg");
// Files.copy(inputStream, destination,
// StandardCopyOption.REPLACE_EXISTING);
System.out.println("found file in request:" + item);
}else{
System.out.println("found string in request:" + new String(item.get(), "UTF-8"));
}
}
} catch (Exception e) {
e.printStackTrace();
}
return "200";
}
I'm using the Camel REST DSL with Restlet and was able to get file uploads working with the following code.
rest("/images").description("Image Upload Service")
.consumes("multipart/form-data").produces("application/json")
.post().description("Uploads image")
.to("direct:uploadImage");
from("direct:uploadImage")
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
MediaType mediaType =
exchange.getIn().getHeader(Exchange.CONTENT_TYPE, MediaType.class);
InputRepresentation representation =
new InputRepresentation(
exchange.getIn().getBody(InputStream.class), mediaType);
try {
List<FileItem> items =
new RestletFileUpload(
new DiskFileItemFactory()).parseRepresentation(representation);
for (FileItem item : items) {
if (!item.isFormField()) {
InputStream inputStream = item.getInputStream();
Path destination = Paths.get("MyFile.jpg");
Files.copy(inputStream, destination,
StandardCopyOption.REPLACE_EXISTING);
}
}
} catch (FileUploadException | IOException e) {
e.printStackTrace();
}
}
});
you can do this with restdsl even if you are not using restlet (exemple jetty) for your restdsl component.
you need to turn restdinding of first for that route and reate two classes to handle the multipart that is in your body.
you need two classes :
DWRequestContext
DWFileUpload
and then you use them in your custom processor
here is the code :
DWRequestContext.java
import org.apache.camel.Exchange;
import org.apache.commons.fileupload.RequestContext;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
public class DWRequestContext implements RequestContext {
private Exchange exchange;
public DWRequestContext(Exchange exchange) {
this.exchange = exchange;
}
public String getCharacterEncoding() {
return StandardCharsets.UTF_8.toString();
}
//could compute here (we have stream cache enabled)
public int getContentLength() {
return (int) -1;
}
public String getContentType() {
return exchange.getIn().getHeader("Content-Type").toString();
}
public InputStream getInputStream() throws IOException {
return this.exchange.getIn().getBody(InputStream.class);
}
}
DWFileUpload.java
import org.apache.camel.Exchange;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUpload;
import org.apache.commons.fileupload.FileUploadException;
import java.util.List;
public class DWFileUpload extends
FileUpload {
public DWFileUpload() {
super();
}
public DWFileUpload(FileItemFactory fileItemFactory) {
super(fileItemFactory);
}
public List<FileItem> parseInputStream(Exchange exchange)
throws FileUploadException {
return parseRequest(new DWRequestContext(exchange));
}
}
you can define your processor like this:
routeDefinition.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
// Create a factory for disk-based file items
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setRepository(new File(System.getProperty("java.io.tmpdir")));
DWFileUpload upload = new DWFileUpload(factory);
java.util.List<FileItem> items = upload.parseInputStream(exchange);
//here I assume I have only one, but I could split it here somehow and link them to camel properties...
//with this, the first file sended with your multipart replaces the body
// of the exchange for the next processor to handle it
exchange.getIn().setBody(items.get(0).getInputStream());
}
});
I stumbled into the same requirement of having to consume a multipart request (containing file data including binary) through Apache Camel Restlet component.
Even though 2.17.x is out, since my project was part of a wider framework / application, I had to be using version 2.12.4.
Initially, my solution drew a lot from restlet-jdbc example yielded data in exchange that although was successfully retrieving text files but I was unable to retrieve correct binary content.
I attempted to dump the data directly into a file to inspect the content using following code (abridged).
from("restlet:/upload?restletMethod=POST")
.to("direct:save-files");
from("direct:save-files")
.process(new org.apache.camel.Processor(){
public void process(org.apache.camel.Exchange exchange){
/*
* Code to sniff exchange content
*/
}
})
.to("file:///C:/<path to a folder>");
;
I used org.apache.commons.fileupload.MultipartStream from apache fileuplaod library to write following utility class to parse Multipart request from a file. It worked successfully when the output of a mulitpart request from Postman was fed to it. However, failed to parse content of the file created by Camel (even through to eyes content of both files looked similar).
public class MultipartParserFileCreator{
public static final String DELIMITER = "\\r?\\n";
public static void main(String[] args) throws Exception {
// taking it from the content-type in exchange
byte[] boundary = "------5lXVNrZvONBWFXxd".getBytes();
FileInputStream fis = new FileInputStream(new File("<path-to-file>"));
extractFile(fis, boundary);
}
public static void extractFile(InputStream is, byte[] boundary) throws Exception {
MultipartStream multipartStream = new MultipartStream(is, boundary, 1024*4, null);
boolean nextPart = multipartStream.skipPreamble();
while (nextPart) {
String headers = multipartStream.readHeaders();
if(isFileContent(headers)) {
String filename = getFileName(headers);
File file = new File("<dir-where-file-created>"+filename);
if(!file.exists()) {
file.createNewFile();
}
FileOutputStream fos = new FileOutputStream(file);
multipartStream.readBodyData(fos);
fos.flush();
fos.close();
}else {
multipartStream.readBodyData(System.out);
}
nextPart = multipartStream.readBoundary();
}
}
public static String[] getContentDispositionTokens(String headersJoined) {
String[] headers = headersJoined.split(DELIMITER, -1);
for(String header: headers) {
System.out.println("Processing header: "+header);
if(header != null && header.startsWith("Content-Disposition:")) {
return header.split(";");
}
}
throw new RuntimeException(
String.format("[%s] header not found in supplied headers [%s]", "Content-Disposition:", headersJoined));
}
public static boolean isFileContent(String header) {
String[] tokens = getContentDispositionTokens(header);
for (String token : tokens) {
if (token.trim().startsWith("filename")) {
return true;
}
}
return false;
}
public static String getFileName(String header) {
String[] tokens = getContentDispositionTokens(header);
for (String token : tokens) {
if (token.trim().startsWith("filename")) {
String filename = token.substring(token.indexOf("=") + 2, token.length()-1);
System.out.println("fileName is " + filename);
return filename;
}
}
return null;
}
}
On debugging through the Camel code, I noticed that at one stage Camel is converting the entire content into String. After a point I had to stop pursuing this approach as there was very little on net applicable for version 2.12.4 and my work was not going anywhere.
Finally, I resorted to following solution
Write an implementation of HttpServletRequestWrapper to allow
multiple read of input stream. One can get an idea from
How to read request.getInputStream() multiple times
Create a filter that uses the above to wrap HttpServletRequest object, reads and extract the file to a directory Convenient way to parse incoming multipart/form-data parameters in a Servlet and attach the path to the request using request.setAttribute() method. With web.xml, configure this filter on restlet servlet
In the process method of camel route, type cast the
exchange.getIn().getBody() in HttpServletRequest object, extract the
attribute (path) use it to read the file as ByteStreamArray for
further processing
Not the cleanest, but I could achieve the objective.

Resources