Download accelerator causes org.apache.catalina.connector.ClientAbortException: java.io.IOException when providing download from backing bean - file

i use JSF and want to have file download in my page . i wrote some codes but i get ClientAbortException error when i use some download manager for download my file :
public class FileUtil {
public static FacesContext getContext() {
return FacesContext.getCurrentInstance();
}
public static void sendFile(File file, boolean attachment) throws IOException {
sendFile(getContext(), file, attachment);
}
public static void sendFile(FacesContext context, File file, boolean attachment) throws IOException {
sendFile(context, new FileInputStream(file), file.getName(), file.length(), attachment);
}
public static void sendFile(FacesContext context, byte[] content, String filename, boolean attachment) throws IOException {
sendFile(context, new ByteArrayInputStream(content), filename, (long) content.length, attachment);
}
public static void sendFile(FacesContext context, InputStream content, String filename, boolean attachment) throws IOException {
sendFile(context, content, filename, -1L, attachment);
}
private static void sendFile(FacesContext context, InputStream input, String filename, long contentLength, boolean attachment) throws IOException {
ExternalContext externalContext = context.getExternalContext();
externalContext.setResponseBufferSize(10240);
externalContext.setResponseContentType(getMimeType(context, filename));
externalContext.setResponseHeader("Content-Disposition", String.format("%s;filename=\"%2$s\"; filename*=UTF-8\'\'%2$s", new Object[]{attachment ? "attachment" : "inline", encodeURL(filename)}));
if (((HttpServletRequest) externalContext.getRequest()).isSecure()) {
externalContext.setResponseHeader("Cache-Control", "public");
externalContext.setResponseHeader("Pragma", "public");
}
if (contentLength != -1L) {
externalContext.setResponseHeader("Content-Length", String.valueOf(contentLength));
}
long size = stream(input, externalContext.getResponseOutputStream());
if (contentLength == -1L) {
externalContext.setResponseHeader("Content-Length", String.valueOf(size));
}
context.responseComplete();
}
public static String getMimeType(FacesContext context, String name) {
String mimeType = context.getExternalContext().getMimeType(name);
if (mimeType == null) {
mimeType = "application/octet-stream";
}
return mimeType;
}
public static long stream(InputStream input, OutputStream output) throws IOException {
ReadableByteChannel inputChannel = Channels.newChannel(input);
Throwable var3 = null;
try {
WritableByteChannel outputChannel = Channels.newChannel(output);
Throwable var5 = null;
try {
ByteBuffer buffer = ByteBuffer.allocateDirect(10240);
long size = 0L;
while (inputChannel.read(buffer) != -1) {
buffer.flip();
size += (long) outputChannel.write(buffer);
buffer.clear();
}
long var9 = size;
return var9;
} catch (Throwable var33) {
var5 = var33;
throw var33;
} finally {
if (outputChannel != null) {
if (var5 != null) {
try {
outputChannel.close();
} catch (Throwable var32) {
var5.addSuppressed(var32);
}
} else {
outputChannel.close();
}
}
}
} catch (Throwable var35) {
var3 = var35;
throw var35;
} finally {
if (inputChannel != null) {
if (var3 != null) {
try {
inputChannel.close();
} catch (Throwable var31) {
var3.addSuppressed(var31);
}
} else {
inputChannel.close();
}
}
}
}
public static String encodeURL(String string) {
if (string == null) {
return null;
} else {
try {
return URLEncoder.encode(string, StandardCharsets.UTF_8.name());
} catch (UnsupportedEncodingException var2) {
throw new UnsupportedOperationException("UTF-8 is apparently not supported on this platform.", var2);
}
}
}
}
something that i can not understand is when download is done by native chorome download without usage of any download manager like IDM or eagleget , I Do not get any ClientAbortException , but when i use these download manager software for (enable their AddOns) i get these error
what happens ? i know this error happens with some connection losing ... but i did not close my page or any thing that cause this error!
and this is my bean code:
#ManagedBean(name = "bean")
#RequestScoped
public class MB implements Serializable {
public void MBdowan() throws IOException {
File file = new File("E:\\Animation\\IA\\Learning movies\\webinar1\\01_Aug_webinar_08\\Aug08_edited_webinar_animation.mov");
FileUtil.sendFile(file,true);
}
and this is my xhtml page :
</h:head>
<h:body>
<h:form>
<p:commandButton value="Download file" ajax="false" actionListener="#{bean.MBdowan}"/>
</h:form>
</h:body>

Download accelerators (and media players!) expect files which are idempotently available via GET and HEAD requests (i.e. when just typing URL in browser's address bar) and preferably also support HTTP Range requests (so multiple HTTP connections could be opened to download parts simultaneously). The JSF backing bean method is only invoked on a POST request (i.e. when submitting a HTML form with method="post"). The ClientAbortException happens because the download accelerator didn't got the response it expected while sniffing for HEAD and Range support and aborted it.
If those files are static and thus not dynamic, then your best bet is to create a separate servlet which supports HEAD and preferably also HTTP Range requests.
Given that you clearly ripped off the source code from OmniFaces Faces#sendFile(), I'd suggest to rip off the source code of another OmniFaces artifact, the FileServlet. You can find snapshot showcase and source code link here: OmniFaces (2.2) FileServlet.
Here's how you could use it:
#WebServlet("/webinar_animation.mov")
public class YourFileServlet extends FileServlet {
#Override
protected File getFile(HttpServletRequest request) throws IllegalArgumentException {
return new File("E:\\Animation\\IA\\Learning movies\\webinar1\\01_Aug_webinar_08\\Aug08_edited_webinar_animation.mov");
}
}
Download file
See also:
How to stream audio/video files such as MP3, MP4, AVI, etc using a Servlet

Related

Reading and writing the file from and to winSCP from the S3 object store

I am trying to put and read file from the remote file system using winSCP through an SFTP connection. The leaf node of the file system is s3 object store which contain the files (for eg: xyz.txt).
Below is the overridden method of File Channel class.
XYZFileSystemProvider
public class XYZFileSystemProvider extends FileSystemProvider {
#Override
public FileChannel newFileChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs)
throws IOException {
// TODO Auto-generated method stub
Collection<XYZOptions.OpenMode> modes = XYZOptions.OpenMode.fromOpenOptions(options);
if (modes.isEmpty()) {
modes = EnumSet.of(XYZOptions.OpenMode.Read, MFEOptions.OpenMode.Write);
}
// TODO: process file attributes
return new XYZFileSystemChannel(path, modes);
}
}
XYZFileSystemChannel
public class XYZFileSystemChannel extends XYZRemotePathChannel{
public XYZFileSystemChannel(XYZPath p, Collection<XYZOptions.OpenMode> modes) throws IOException {
this(Objects.requireNonNull(p, "No target path").toString(), p.getFileSystem(), modes);
}
public XYZFileSystemChannel(String remotePath, XYZFileSystem fs, Collection<XYZOptions.OpenMode> modes) throws IOException {
super(remotePath, fs, true, modes);
}
}
XYZRemotePathChannel
public class XYZRemotePathChannel extends FileChannel {
private AmazonS3Component getAmazonS3Instance() {
return SpringContext.getBean(AmazonS3Component.class);
}
private final String path;
private final Collection<XYZOptions.OpenMode> modes;
private final boolean closeOnExit;
private XYZFileSystem fileSystem;
private final AtomicLong posTracker = new AtomicLong(0L);
public static final Set<XYZOptions.OpenMode> READ_MODES =
Collections.unmodifiableSet(EnumSet.of(XYZOptions.OpenMode.Read));
private final Object lock = new Object();
private final AtomicReference<Thread> blockingThreadHolder = new AtomicReference<>(null);
public XYZRemotePathChannel(String path, XYZFileSystem fileSystem, boolean closeOnExit,
Collection<XYZOptions.OpenMode> modes) throws IOException {
this.path = ValidateUtils.checkNotNullAndNotEmpty(path, "No remote file path specified");
this.modes = Objects.requireNonNull(modes, "No channel modes specified");
this.closeOnExit = closeOnExit;
this.fileSystem = fileSystem;
}
#Override
public int read(ByteBuffer dst) throws IOException {
// TODO Auto-generated method stub
log.debug("Position of dst is : {}",dst.position());
log.debug("Reading the bytes of the file : {}", dst);
//Some code to be done here in order to read dst and send bytes of the file recieved from s3 store
return (int) doRead(Collections.singletonList(dst), -1);
}
protected long doRead(List<ByteBuffer> buffers, long position) throws IOException {
log.debug("Do Reading the bytes of the file of list of buffer : {} and position :{}", buffers , position);
ensureOpen(READ_MODES);
synchronized (lock) {
boolean completed = false;
boolean eof = false;
long curPos = (position >= 0L) ? position : posTracker.get();
byte[] bytes = new byte[(int) curPos];
try {
long totalRead = 0;
beginBlocking();
String [] parts = this.path.toString().replaceFirst("^/", "").split("/");
String bucket = parts[parts.length-2];
String fileName = parts[parts.length-1];
InputStream fileContent = getAmazonS3Instance().getFileFromBucket(bucket, fileName);
log.debug("Contens of the file: {} from bucket: {} are : {}", fileName , bucket, fileContent);
//Some code to be done here to return the content byte length??
int fileLenght = fileContent.read(bytes, 1, (int) curPos);
log.debug("After reading the file content the file length is : {}" , fileLenght );
return fileLenght;
} finally {
if (position < 0L) {
posTracker.set(curPos);
}
endBlocking(completed);
}
}
}
private void endBlocking(boolean completed) throws AsynchronousCloseException {
blockingThreadHolder.set(null);
end(completed);
}
private void beginBlocking() {
begin();
blockingThreadHolder.set(Thread.currentThread());
}
#Override
public FileChannel position(long newPosition) throws IOException {
// TODO Auto-generated method stub
log.debug("Setting the position of the file : {}", newPosition);
if (newPosition < 0L) {
throw new IllegalArgumentException("position(" + this.path + ") illegal file channel position: " + newPosition);
}
ensureOpen(Collections.emptySet());
posTracker.set(newPosition);
return this;
}
private void ensureOpen(Collection<XYZOptions.OpenMode> reqModes) throws IOException {
if (!isOpen()) {
throw new ClosedChannelException();
}
if (GenericUtils.size(reqModes) > 0) {
for (XYZOptions.OpenMode m : reqModes) {
if (this.modes.contains(m)) {
return;
}
}
throw new IOException("ensureOpen(" + this.path + ") current channel modes (" + this.modes
+ ") do contain any of the required: " + reqModes);
}
}
}
XYZOptions
public class XYZOptions {
enum OpenMode {
Read, Write, Append, Create, Truncate, Exclusive;
public static final Set<OpenOption> SUPPORTED_OPTIONS = Collections
.unmodifiableSet(EnumSet.of(StandardOpenOption.READ, StandardOpenOption.APPEND,
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE,
StandardOpenOption.CREATE_NEW, StandardOpenOption.SPARSE));
public static Set<OpenMode> fromOpenOptions(Collection<? extends OpenOption> options) {
if (GenericUtils.isEmpty(options)) {
return Collections.emptySet();
}
Set<OpenMode> modes = EnumSet.noneOf(OpenMode.class);
for (OpenOption option : options) {
if (option == StandardOpenOption.READ) {
modes.add(Read);
} else if (option == StandardOpenOption.APPEND) {
modes.add(Append);
} else if (option == StandardOpenOption.CREATE) {
modes.add(Create);
} else if (option == StandardOpenOption.TRUNCATE_EXISTING) {
modes.add(Truncate);
} else if (option == StandardOpenOption.WRITE) {
modes.add(Write);
} else if (option == StandardOpenOption.CREATE_NEW) {
modes.add(Create);
modes.add(Exclusive);
} else if (option == StandardOpenOption.SPARSE) {
continue;
} else {
throw new IllegalArgumentException("Unsupported open option: " + option);
}
}
return modes;
}
}
}
I am able to fetch the file from the s3 store but nor sure how to read and pass all the contents while someone drag and drop from remote file location to their own system using winSCP. I know i am missing some code at the mentioned place but not sure how to achieve it.

How to open a PDF file downloaded and stored in the Download folder an a mobile device?

I've researched different solutions to this problem, but none of them works for me. I am trying to download a file from Firebase (which I am successful in doing) and then I am trying to open that file in my app right after the download completes. However, my app either crashes or does nothing.
Below is the code for downloading the file (from FirebaseStorage which works):
public void download(String name) {
final String pdf_name = name.substring(0, name.lastIndexOf('.'));
storageReference = firebaseStorage.getInstance().getReference();
ref=storageReference.child("Auctions/" + name);
ref.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
String url = uri.toString();
downloadFile(ActiveAuctionsActivity.this, pdf_name, ".pdf", DIRECTORY_DOWNLOADS, url);
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
SpannableString spannableString = new SpannableString("אין תיק עבודה למכרז זה");
spannableString.setSpan(
new ForegroundColorSpan(getResources().getColor(android.R.color.holo_red_light)),
0,
spannableString.length(),
0);
Toast.makeText(ActiveAuctionsActivity.this, spannableString, Toast.LENGTH_LONG).show();
}
});
}
public void downloadFile(Context context, String fileName, String fileExtention, String destinationDirectory, String url){
DownloadManager downloadmanager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
Uri uri = Uri.parse(url);
DownloadManager.Request request = new DownloadManager.Request(uri);
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setDescription("מוריד.....");
//request.setDestinationInExternalFilesDir(context, destinationDirectory, fileName + fileExtention);
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName + fileExtention);
// call allowScanningByMediaScanner() to allow media scanner to discover your file
request.allowScanningByMediaScanner();
downloadmanager.enqueue(request);
registerReceiver(onComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
Toast.makeText(getApplicationContext(), "מוריד את התיק העבודה.....",
Toast.LENGTH_SHORT).show();
}
After, I setup the receiver with the openFile() method:
BroadcastReceiver onComplete=new BroadcastReceiver() {
public void onReceive(Context ctxt, Intent intent) {
Toast.makeText(getApplicationContext(), "ההורדה הסתיימה",
Toast.LENGTH_LONG).show();
openFile("GMU.pdf");
}
};
public void openFile(String fileName){
try {
File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), fileName);
Uri path = Uri.fromFile(file);
Log.i("Fragment2", String.valueOf(path));
Intent pdfOpenintent = new Intent(Intent.ACTION_VIEW);
pdfOpenintent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
pdfOpenintent.setDataAndType(path, "application/pdf");
this.startActivity(pdfOpenintent);
} catch (ActivityNotFoundException e) {
Toast.makeText(ActiveAuctionsActivity.this, "error", Toast.LENGTH_LONG).show();
}
}
Again, the file does download, but doesn't open.
What am I doing wrong, could you please advise me?
EDIT
I also tried the code below as my openFile() but that also doesn't work:
File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), fileName);
Uri path = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".provider", file);
Intent pdfOpenintent = new Intent(Intent.ACTION_VIEW);
pdfOpenintent.setDataAndType(path, pdfOpenintent.getType());
pdfOpenintent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
pdfOpenintent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
try {
ActiveAuctionsActivity.this.startActivity(pdfOpenintent);
} catch (ActivityNotFoundException e) {
pdfOpenintent.setType("application/*");
startActivity(Intent.createChooser(pdfOpenintent, "No Application found to open File - " + fileName));
}

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.

Easy way to dynamically invoke web services (without JDK or proxy classes)

In Python I can consume a web service so easily:
from suds.client import Client
client = Client('http://www.example.org/MyService/wsdl/myservice.wsdl') #create client
result = client.service.myWSMethod("Bubi", 15) #invoke method
print result #print the result returned by the WS method
I'd like to reach such a simple usage with Java.
With Axis or CXF you have to create a web service client, i.e. a package which reproduces all web service methods so that we can invoke them as if they where normal methods. Let's call it proxy classes; usually they are generated by wsdl2java tool.
Useful and user-friendly. But any time I add/modify a web service method and I want to use it in a client program I need to regenerate proxy classes.
So I found CXF DynamicClientFactory, this technique avoids the use of proxy classes:
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.endpoint.dynamic.DynamicClientFactory;
//...
//create client
DynamicClientFactory dcf = DynamicClientFactory.newInstance();
Client client = dcf.createClient("http://www.example.org/MyService/wsdl/myservice.wsdl");
//invoke method
Object[] res = client.invoke("myWSMethod", "Bubi");
//print the result
System.out.println("Response:\n" + res[0]);
But unfortunately it creates and compiles proxy classes runtime, hence requires JDK on the production machine. I have to avoid this, or at least I can't rely on it.
My question:
Is there another way to dinamically invoke any method of a web service in Java, without having a JDK at runtime and without generating "static" proxy classes? Maybe with a different library? Thanks!
I know this is a really old question but if you are still interested you could use soap-ws github project: https://github.com/reficio/soap-ws
Here you have a sample usage really simple:
Wsdl wsdl = Wsdl.parse("http://www.webservicex.net/CurrencyConvertor.asmx?WSDL");
SoapBuilder builder = wsdl.binding()
.localPart("CurrencyConvertorSoap")
.find();
SoapOperation operation = builder.operation()
.soapAction("http://www.webserviceX.NET/ConversionRate")
.find();
Request request = builder.buildInputMessage(operation)
SoapClient client = SoapClient.builder()
.endpointUrl("http://www.webservicex.net/CurrencyConvertor.asmx")
.build();
String response = client.post(request);
As you can see it is really simple.
With CXF 3.x this could be possible with StaxDataBinding. Follow below steps to get the basics. Of course, this could be enhanced to your needs.
Create StaxDataBinding something like below. Note below code can be enhanced to your sophistication.
class StaxDataBinding extends AbstractInterceptorProvidingDataBinding {
private XMLStreamDataReader xsrReader;
private XMLStreamDataWriter xswWriter;
public StaxDataBinding() {
super();
this.xsrReader = new XMLStreamDataReader();
this.xswWriter = new XMLStreamDataWriter();
inInterceptors.add(new StaxInEndingInterceptor(Phase.POST_INVOKE));
inFaultInterceptors.add(new StaxInEndingInterceptor(Phase.POST_INVOKE));
inInterceptors.add(RemoveStaxInEndingInterceptor.INSTANCE);
inFaultInterceptors.add(RemoveStaxInEndingInterceptor.INSTANCE);
}
static class RemoveStaxInEndingInterceptor
extends AbstractPhaseInterceptor<Message> {
static final RemoveStaxInEndingInterceptor INSTANCE = new RemoveStaxInEndingInterceptor();
public RemoveStaxInEndingInterceptor() {
super(Phase.PRE_INVOKE);
addBefore(StaxInEndingInterceptor.class.getName());
}
public void handleMessage(Message message) throws Fault {
message.getInterceptorChain().remove(StaxInEndingInterceptor.INSTANCE);
}
}
public void initialize(Service service) {
for (ServiceInfo serviceInfo : service.getServiceInfos()) {
SchemaCollection schemaCollection = serviceInfo.getXmlSchemaCollection();
if (schemaCollection.getXmlSchemas().length > 1) {
// Schemas are already populated.
continue;
}
new ServiceModelVisitor(serviceInfo) {
public void begin(MessagePartInfo part) {
if (part.getTypeQName() != null
|| part.getElementQName() != null) {
return;
}
part.setTypeQName(Constants.XSD_ANYTYPE);
}
}.walk();
}
}
#SuppressWarnings("unchecked")
public <T> DataReader<T> createReader(Class<T> cls) {
if (cls == XMLStreamReader.class) {
return (DataReader<T>) xsrReader;
}
else {
throw new UnsupportedOperationException(
"The type " + cls.getName() + " is not supported.");
}
}
public Class<?>[] getSupportedReaderFormats() {
return new Class[] { XMLStreamReader.class };
}
#SuppressWarnings("unchecked")
public <T> DataWriter<T> createWriter(Class<T> cls) {
if (cls == XMLStreamWriter.class) {
return (DataWriter<T>) xswWriter;
}
else {
throw new UnsupportedOperationException(
"The type " + cls.getName() + " is not supported.");
}
}
public Class<?>[] getSupportedWriterFormats() {
return new Class[] { XMLStreamWriter.class, Node.class };
}
public static class XMLStreamDataReader implements DataReader<XMLStreamReader> {
public Object read(MessagePartInfo part, XMLStreamReader input) {
return read(null, input, part.getTypeClass());
}
public Object read(QName name, XMLStreamReader input, Class<?> type) {
return input;
}
public Object read(XMLStreamReader reader) {
return reader;
}
public void setSchema(Schema s) {
}
public void setAttachments(Collection<Attachment> attachments) {
}
public void setProperty(String prop, Object value) {
}
}
public static class XMLStreamDataWriter implements DataWriter<XMLStreamWriter> {
private static final Logger LOG = LogUtils
.getL7dLogger(XMLStreamDataWriter.class);
public void write(Object obj, MessagePartInfo part, XMLStreamWriter writer) {
try {
if (!doWrite(obj, writer)) {
// WRITE YOUR LOGIC HOW you WANT TO HANDLE THE INPUT DATA
//BELOW CODE JUST CALLS toString() METHOD
if (part.isElement()) {
QName element = part.getElementQName();
writer.writeStartElement(element.getNamespaceURI(),
element.getLocalPart());
if (obj != null) {
writer.writeCharacters(obj.toString());
}
writer.writeEndElement();
}
}
}
catch (XMLStreamException e) {
throw new Fault("COULD_NOT_READ_XML_STREAM", LOG, e);
}
}
public void write(Object obj, XMLStreamWriter writer) {
try {
if (!doWrite(obj, writer)) {
throw new UnsupportedOperationException("Data types of "
+ obj.getClass() + " are not supported.");
}
}
catch (XMLStreamException e) {
throw new Fault("COULD_NOT_READ_XML_STREAM", LOG, e);
}
}
private boolean doWrite(Object obj, XMLStreamWriter writer)
throws XMLStreamException {
if (obj instanceof XMLStreamReader) {
XMLStreamReader xmlStreamReader = (XMLStreamReader) obj;
StaxUtils.copy(xmlStreamReader, writer);
xmlStreamReader.close();
return true;
}
else if (obj instanceof XMLStreamWriterCallback) {
((XMLStreamWriterCallback) obj).write(writer);
return true;
}
return false;
}
public void setSchema(Schema s) {
}
public void setAttachments(Collection<Attachment> attachments) {
}
public void setProperty(String key, Object value) {
}
}
}
Prepare your input to match the expected input, something like below
private Object[] prepareInput(BindingOperationInfo operInfo, String[] paramNames,
String[] paramValues) {
List<Object> inputs = new ArrayList<Object>();
List<MessagePartInfo> parts = operInfo.getInput().getMessageParts();
if (parts != null && parts.size() > 0) {
for (MessagePartInfo partInfo : parts) {
QName element = partInfo.getElementQName();
String localPart = element.getLocalPart();
// whatever your input data you need to match data value for given element
// below code assumes names are paramNames variable and value in paramValues
for (int i = 0; i < paramNames.length; i++) {
if (paramNames[i].equals(localPart)) {
inputs.add(findParamValue(paramNames, paramValues, localPart));
}
}
}
}
return inputs.toArray();
}
Now set the proper data binding and pass the data
Bus bus = CXFBusFactory.getThreadDefaultBus();
WSDLServiceFactory sf = new WSDLServiceFactory(bus, wsdl);
sf.setAllowElementRefs(false);
Service svc = sf.create();
Client client = new ClientImpl(bus, svc, null,
SimpleEndpointImplFactory.getSingleton());
StaxDataBinding databinding = new StaxDataBinding();
svc.setDataBinding(databinding);
bus.getFeatures().add(new StaxDataBindingFeature());
BindingOperationInfo operInfo = ...//find the operation you need (see below)
Object[] inputs = prepareInput(operInfo, paramNames, paramValues);
client.invoke("operationname", inputs);
If needed you can match operation name something like below
private BindingOperationInfo findBindingOperation(Service service,
String operationName) {
for (ServiceInfo serviceInfo : service.getServiceInfos()) {
Collection<BindingInfo> bindingInfos = serviceInfo.getBindings();
for (BindingInfo bindingInfo : bindingInfos) {
Collection<BindingOperationInfo> operInfos = bindingInfo.getOperations();
for (BindingOperationInfo operInfo : operInfos) {
if (operInfo.getName().getLocalPart().equals(operationName)) {
if (operInfo.isUnwrappedCapable()) {
return operInfo.getUnwrappedOperation();
}
return operInfo;
}
}
}
}
return null;
}

FileInputFormat where filename is KEY and text contents are VALUE

I'd like to use an entire file as a single record for MAP processing, with the filename as the key.
I've read the following post: How to get Filename/File Contents as key/value input for MAP when running a Hadoop MapReduce Job?
and while the theory of the top answer is solid, no code or "how-to" is actually provided.
Here is my custom FileInputFormat and the corresponding RecordReader, which compile, yet do not produce ANY record data.
Thanks for any help.
public class CommentsInput
extends FileInputFormat<Text,Text> {
protected boolean isSplitable(FileSystem fs, Path filename)
{
return false;
}
#Override
public RecordReader<Text, Text> createRecordReader(InputSplit split, TaskAttemptContext ctx)
throws IOException, InterruptedException {
return new CommentFileRecordReader((FileSplit) split, ctx.getConfiguration());
}
/////////////////////////
public class CommentFileRecordReader
extends RecordReader<Text,Text> {
private InputStream in;
private long start;
private long length;
private long position;
private Text key;
private Text value;
private boolean processed;
private FileSplit fileSplit;
private Configuration conf;
public CommentFileRecordReader(FileSplit fileSplit, Configuration conf) throws IOException
{
this.fileSplit = fileSplit;
this.conf=conf;
}
/** Boilerplate initialization code for file input streams. */
#Override
public void initialize(InputSplit split,
TaskAttemptContext context)
throws IOException, InterruptedException {
Configuration conf = context.getConfiguration();
fileSplit = (FileSplit) split;
this.start = fileSplit.getStart();
this.length = fileSplit.getLength();
this.position = 0;
this.processed = false;
Path path = fileSplit.getPath();
FileSystem fs = path.getFileSystem(conf);
FSDataInputStream in = fs.open(path);
CompressionCodecFactory codecs = new CompressionCodecFactory(conf);
CompressionCodec codec = codecs.getCodec(path);
if (codec != null)
this.in = codec.createInputStream(in);
else
this.in = in;
// If using Writables:
// key = new Text();
// value = new Text();
}
public boolean next(Text key, Text value) throws IOException
{
if(!processed)
{
key = new Text(fileSplit.getPath().toString());
Path file = fileSplit.getPath();
FileSystem fs = file.getFileSystem(conf);
FSDataInputStream in = null;
byte[] contents = new byte[(int) fileSplit.getLength()];
try
{
in = fs.open(file);
IOUtils.readFully(in, contents, 0, contents.length);
value.set(contents.toString());
}
finally
{
IOUtils.closeStream(in);
}
processed = true;
return true;
}
return false;
}
#Override
public boolean nextKeyValue() throws IOException {
// TODO parse the next key value, update position and return true.
return false;
}
#Override
public Text getCurrentKey() {
return key;
}
#Override
public Text getCurrentValue() {
return value;
}
/** Returns our progress within the split, as a float between 0 and 1. */
#Override
public float getProgress() {
if (length == 0)
return 0.0f;
return Math.min(1.0f, position / (float)length);
}
#Override
public void close() throws IOException {
if (in != null)
in.close();
}
}
You need to find a way to define your own key class and make sure your classes use it. You can look up how to define your own key class and you can get a file name by calling hte getName() method on its path then use it to make your key.

Resources