PDF file download using BlockingQueue - java

I'm trying to download a pdf file using URLConnection. Here's how I setup the connection object.
URL serverUrl = new URL(url);
urlConnection = (HttpURLConnection) serverUrl.openConnection();
urlConnection.setDoInput(true);
urlConnection.setRequestMethod("GET");
urlConnection.setRequestProperty("Content-Type", "application/pdf");
urlConnection.setRequestProperty("ENCTYPE", "multipart/form-data");
String contentLength = urlConnection.getHeaderField("Content-Length");
I obtained inputstream from the connection object.
bufferedInputStream = new BufferedInputStream(urlConnection.getInputStream());
And the output stream to write the file contents.
File dir = new File(context.getFilesDir(), mFolder);
if(!dir.exists()) dir.mkdir();
final File f = new File(dir, String.valueOf(documentName));
f.createNewFile();
final BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(f, true)); //true for appendMode
BlockingQueue is created so that threads performing read and write operations can access the queue.
final BlockingQueue<ByteArrayWrapper> blockingQueue = new ArrayBlockingQueue<ByteArrayWrapper>(MAX_VALUE,true);
final byte[] dataBuffer = new byte[MAX_VALUE];
Now created thread to read data from InputStream.
Thread readerThread = new Thread(new Runnable() {
#Override
public void run() {
try {
int count = 0;
while((count = bufferedInputStream.read(dataBuffer, 0, dataBuffer.length)) != -1) {
ByteArrayWrapper byteArrayWrapper = new ByteArrayWrapper(dataBuffer);
byteArrayWrapper.setBytesReadCount(count);
blockingQueue.put(byteArrayWrapper);
}
blockingQueue.put(null); //end of file
} catch(Exception e) {
e.printStackTrace();
} finally {
try {
bufferedInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
Now the writer thread reads those file contents.
Thread writerThread = new Thread(new Runnable() {
#Override
public void run() {
try {
while(true) {
ByteArrayWrapper byteWrapper = blockingQueue.take();
if(null == byteWrapper) break;
bufferedOutputStream.write(byteWrapper.getBytesRead(), 0, byteWrapper.getBytesReadCount());
}
bufferedOutputStream.flush();
} catch(Exception e) {
e.printStackTrace();
} finally {
try {
bufferedOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
Finally, threads are started.
readerThread.start();
writerThread.start();
Theoretically it should read the file from InputStream and save it to the target file. However, in reality, it produces blank pdf file. At some other time, it shows invalid pdf format exception. File size matches with content length of the InputStream. Is there anything I'm missing?

I'm not familiar with ByteArrayWrapper. Does it just hold a reference to the array, like this?
public class ByteArrayBuffer {
final private byte[] data;
public ByteArrayBuffer(byte[] data) {
this.data = data;
}
public byte[] getBytesRead() {
return data;
}
/*...etc...*/
}
If so. that would be the problem: all of the ByteArrayWrapper objects are backed by the same array. Which is repeatedly overwritten by the writer. Even though BlockingQueue did the hard work of safely publishing each object from one thread to the other.
The simplest fix might be to make the ByteArrayWrapper effectively immutable i.e. don't change it after publishing it to another thread. Taking a copy of the array on construction would be simplest:
public ByteArrayWrapper(byte[] data) {
this.data = Arrays.copyOf(data, data.length);
}
One other problem is that "BlockingQueue does not accept null elements" (see BlockingQueue docs), and so the "end of input" sentinel value doesn't work. Replacing null with a
private static ByteArrayWrapper END = new ByteArrayWrapper(new byte[]{});
in the appropriate places will fix that.
By making those changes to a copy of the code I was able to retrieve a faithful copy of a PDF file.

Try to use Android DownloadManager (http://developer.android.com/reference/android/app/DownloadManager.html) it is used to handle long-running HTTP requests in the background.
Here you don't need to think about received bytes and the progress is displayed in the notification bar.
There is a good tutorial here: http://blog.vogella.com/2011/06/14/android-downloadmanager-example/

Related

Is it possible to load Executable once and keep calling it multiple times in Java?

I am working on prototype where in from my Java API I have to run an executable which in C#. There is code which inturn calls Matlab function.
Following is the java code to call the executable(an example)
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ByteArrayOutputStream errorStream = new ByteArrayOutputStream();
matProcessWrapper = new ExecWrapper.ExecWrapperBuilder
("C:\\Matlab\\HelloWorld\\bin\\Release\\netcoreapp3.1\\HelloWorld.exe")
.setErrorStream(errorStream)
.setOutputStream(outputStream)
.setTimeOutMilliSeconds(30*1000L)
.build();
try {
matProcessWrapper.executeProcessSync();
} catch (IOException e) {
e.printStackTrace();
}
The executable is loaded each time. is it possible to load this executable only once and then call its method again and again and once all the calling is done I can exit the model.
You could check if your process wrapper is already loaded or load it in your constructor.
public void execute() {
if (matProcessWrapper == null) {
loadMatProcessWrapper();
}
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ByteArrayOutputStream errorStream = new ByteArrayOutputStream()) {
matProcessWrapper.executeProcessSync();
} catch (IOException e) {
e.printStackTrace();
}
}
public void loadMatProcessWrapper() {
matProcessWrapper = new ExecWrapper.ExecWrapperBuilder
("C:\\Matlab\\HelloWorld\\bin\\Release\\netcoreapp3.1\\HelloWorld.exe")
.setErrorStream(errorStream)
.setOutputStream(outputStream)
.setTimeOutMilliSeconds(30 * 1000L)
.build();
}
Also don't forget to close your streams, I did this in my code snippet with try with resources.

How to avoid java.io.StreamCorruptedException?

I have a method that writes data from a list to a file, a method that reads data from a file into a list and a method that writes data from a list in a file to the specified number of times. I'm trying to extract data from a file after I use the first method writeFile () everything works fine. I read the data from the file into the list by readFile () method. After that I use my method which writes to the file the number of times I need, everything is fine, it writes multyWrite (). But after that I can not read the data from the file in the readFile () method since I get `
Exception stack trace:
Exception in thread "main" java.io.StreamCorruptedException: invalid type code: AC
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1599)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)
at ProductService.readFile(ProductService.java:47)
at Main.main(Main.java:21)
I know that I should use objectOutputStream.reset (), but where would it be better to use it?
private String fileName;
private ProductInterface<FlyingMachine> productService = new ProductInterfaceImpl();
private ObjectOutputStream objectOutputStream;
private FileOutputStream fileOutputStream;
public ProductService(String fileName) throws IOException {
this.fileName = fileName;
fileOutputStream = new FileOutputStream(fileName);
this.objectOutputStream = new ObjectOutputStream(fileOutputStream);
}
public void writeFile() throws IOException {
try {
for (FlyingMachine f : productService.getProductContainer()) {
objectOutputStream.writeObject(f);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (objectOutputStream != null) {
objectOutputStream.flush();
objectOutputStream.close();
fileOutputStream.close();
}
}
}`
public void readFile() throws IOException {
ObjectInputStream objectInputStream = null;
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(fileName);
objectInputStream = new ObjectInputStream(fileInputStream);
while (fileInputStream.available() > 0) {
FlyingMachine flyingMachine = (FlyingMachine) objectInputStream.readObject();
productService.getProductContainer().add(flyingMachine);
}
} catch (ClassNotFoundException | EOFException e) {
e.printStackTrace();
} finally {
if (objectInputStream != null) {
objectInputStream.close();
fileInputStream.close();
}
}
}
public void multyWrite(int number) throws IOException {
for (int i = 0; i < number; i++) {
try {
fileOutputStream = new FileOutputStream(fileName, true);
objectOutputStream = new ObjectOutputStream(fileOutputStream);
for (FlyingMachine f : productService.getProductContainer()) {
objectOutputStream.writeObject(f);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (objectOutputStream != null) {
objectOutputStream.flush();
objectOutputStream.close();
}
}
}
}
You create a new ObjectOutputStream in the constructor. In writeFile you use that OOS instance and close it. But in multyWrite you don't use it and instead create new instances.
Now when you call multyWrite without having called writeFile first, that first OOS will still be open, but the OOS you create in multyWrite doesn't know that - thus causing your file to have two OOS headers after another.
And then when you try to read such a file, the ObjectInputStream will find the first header (all is fine) and then unexpectedly find the second header, while it expected a type code. That header starts with 0xAC, hence throwing the exception message "invalid type code: AC".
To fix this, either have multyWrite use the OOS constructed in your constructor, the same way writeFile does, or make sure that that OOS is closed before you create a new one.
It's generally not a good idea to open a stream (of any kind) in a constructor and then rely on external code calling a specific method to close it. Better create streams when you need them and close them directly.

Thread race condition just hangs while using PipedOutputStream

I am using piped output streams to convert OutputStream to InputStream because the AWS java sdk does not allow puting objects on S3 using OutputStreams
I'm using the code below, however, this will intermittently just hang. This code is in a web application. Currently there is no load on the application...I am just trying it out on my personal computer.
ByteArrayOutputStream os = new ByteArrayOutputStream();
PipedInputStream inpipe = new PipedInputStream();
final PipedOutputStream out = new PipedOutputStream(inpipe);
try {
String xmpXml = "<dc:description>somedesc</dc:description>"
JpegXmpRewriter rewriter = new JpegXmpRewriter();
rewriter.updateXmpXml(isNew1,os, xmpXml);
new Thread(new Runnable() {
public void run () {
try {
// write the original OutputStream to the PipedOutputStream
println "starting writeto"
os.writeTo(out);
out.close();
println "ending writeto"
} catch (IOException e) {
System.out.println("Some exception)
}
}
}).start();
ObjectMetadata metadata1 = new ObjectMetadata();
metadata1.setContentLength(os.size());
client.putObject(new PutObjectRequest("test-bucket", "167_sample.jpg", inpipe, metadata1));
}
catch (Exception e) {
System.out.println("Some exception")
}
finally {
isNew1.close()
os.close()
}
Instead of bothering with the complexities of starting another thread, instantiating two concurrent classes, and then passing data from thread to thread, all to solve nothing but a minor limitation in the provided JDK API, you should just create a simple specialization of the ByteArrayOutputStream:
class BetterByteArrayOutputStream extends ByteArrayOutputStream {
public ByteArrayInputStream toInputStream() {
return new ByteArrayInputStream(buf, 0, count);
}
}
This converts it to an input stream with no copying.

converting a java.util.stream.Stream<String> into a java.io.Reader

Part of my application is given an InputStream and wants to do some processing on this to produce another InputStream.
try (
final BufferedReader inputReader = new BufferedReader(new InputStreamReader(inputStream, UTF_8), BUFFER_SIZE);
final Stream<String> resultLineStream = inputReader.lines().map(lineProcessor::processLine);
final InputStream resultStream = new ReaderInputStream(new StringStreamReader(resultLineStream), UTF_8);
) {
s3Client.putObject(targetBucket, s3File, resultStream, new ObjectMetadata());
} catch (IOException e) {
throw new RuntimeException("Exception", e);
}
I am using the new Java 8 BufferedReader.lines() to a Stream onto which I can easily map my processing function.
The only thing still lacking is class StringStreamReader() which is supposed to turn my Stream into a Reader from which Apache commons-io:ReaderInputStream can create an InputStream again. (The detour to readers and back seems reasonable to deal with encodings and line breaks.)
To be very clear, the code above assumes
public class StringStreamReader extends Reader {
public StringStreamReader(Stream<String> stringStream) { ... }
#Overwrite
public int read(char cbuf[], int off, int len) throws IOException { ... }
// possibly overwrite other methods to avoid bad performance or high resource-consumption
}
So is there any library that offers such a StringStreamReader class? Or this there another way to write the application code above without implementing a custom Reader or InputStream subclass?
You can do something like that:
PipedWriter writer = new PipedWriter();
PipedReader reader = new PipedReader();
reader.connect(writer);
strings.stream().forEach(string -> {
try {
writer.write(string);
writer.write("\n");
} catch (Exception e) {
e.printStackTrace();
}
});
But i guess you want some form of lazy processing. Stream api does not really help in that case, you need a dedicated Thread + some buffer to do that.

How to cache/save custom class object in Android?

I want to save ArrayList<CustomClass>-object to somewhere in Android storage to retrieve quickly and display data in it.
Is this possible or not? If yes, then which technique will be suitable, SQLite or external storage?
example.
public class MyClass implements Serializable
{
private static final long serialVersionUID = 1L;
public String title;
public String startTime;
public String endTime;
public String day;
public boolean classEnabled;
public MyClass(String title, String startTime, boolean enable) {
this.title = title;
this.startTime = startTime;
this.classEnabled = enable;
}
public boolean saveObject(MyClass obj) {
final File suspend_f=new File(SerializationTest.cacheDir, "test");
FileOutputStream fos = null;
ObjectOutputStream oos = null;
boolean keep = true;
try {
fos = new FileOutputStream(suspend_f);
oos = new ObjectOutputStream(fos);
oos.writeObject(obj);
} catch (Exception e) {
keep = false;
} finally {
try {
if (oos != null) oos.close();
if (fos != null) fos.close();
if (keep == false) suspend_f.delete();
} catch (Exception e) { /* do nothing */ }
}
return keep;
}
public MyClass getObject(Context c) {
final File suspend_f=new File(SerializationTest.cacheDir, "test");
MyClass simpleClass= null;
FileInputStream fis = null;
ObjectInputStream is = null;
try {
fis = new FileInputStream(suspend_f);
is = new ObjectInputStream(fis);
simpleClass = (MyClass) is.readObject();
} catch(Exception e) {
String val= e.getMessage();
} finally {
try {
if (fis != null) fis.close();
if (is != null) is.close();
} catch (Exception e) { }
}
return simpleClass;
}
and calling from activity
if(android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
cacheDir=new File(android.os.Environment.getExternalStorageDirectory(),"MyCustomObject");
else
cacheDir= getCacheDir();
if(!cacheDir.exists())
cacheDir.mkdirs();
MyClass m = new MyClass("umer", "asif", true);
boolean result = m.saveObject(m);
if(result)
Toast.makeText(this, "Saved object", Toast.LENGTH_LONG).show();
else
Toast.makeText(this, "Error saving object", Toast.LENGTH_LONG).show();
MyClass m = new MyClass();
MyClass c = m.getObject(this);
if(c!= null)
Toast.makeText(this, "Retrieved object", Toast.LENGTH_LONG).show();
else
Toast.makeText(this, "Error retrieving object", Toast.LENGTH_LONG).show();
dont forget to use write_external_storage permissions in manifest file.
This problem isn't really android specific. I mean if you know how to serialize your data either via java.io.Serializable or you have a custom persistence format, you just need to know where to store it.
You can grab a file on the local device via
FileOutputStream stream = null;
try{
stream = mContext.openFileOutput("name.data", Context.MODE_PRIVATE);
ObjectOutputStream dout = new ObjectOutputStream(stream);
dout.writeObject(myArrayList);
dout.flush();
stream.getFD().sync();
} catch(IOException e) { //Do something intelligent
} finally {
stream.close();
}
You will have to use openFileInput() later to read the data back.
Or you can grab External Storage. This is similar however you have to guarantee that it even exists. Like is the external storage connected and even able to be written to. Since you are writing a data structure here and usually external storage is world readable I don't think this is a good idea for your intended purposes (just from what you have put so far).
If your data is structured and is going to be queried many times and it might be rather big to always load up then consider using the sql lite tools that are part of the OS. However I am assuming you don't need this either as a simple list is just that, a linear structures which you could probably seek through in a file (assuming it is less than 1MB of data :)
This issue can be solved by singleton class here in which you can set/get any object eg arrayList in any time. If you used Android Service then this solutuion is not suitable.
Perhaps the solution to your problem can be found here.

Categories