There is a task to read a text file in a producer paradigm. The producer interface is defined as the following:
public interface Producer<ITEM> {
/**
* Produces the next item.
*
* #return produced item
*/
ITEM next();
/**
* Tells if there are more items available.
*
* #return true if there are more items, false otherwise
*/
boolean hasNext();
}
Current code to read the text file is:
public static void readTextFile(File file, Charset charset, Consumer<String> consumer) {
try (InputStreamReader isr = new InputStreamReader(new FileInputStream(file), charset);
BufferedReader in = new BufferedReader(isr, BUFFER_SIZE)) {
String line;
while ((line = in.readLine()) != null) {
consumer.accept(line);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
The task is to convert it into a:
public static Producer<String> readTextFileRetProducer(File file, Charset charset) {
// ???
return null;
}
Then there goes a list of issues:
How to support hasNext properly given that it requires reading a text line in advance.
How to properly manage exceptions?
How to properly release external resources given that a handy try-with-resources block would no longer be available in the producer paradigm?
P.S. Resources are to be released after the last line from the file has been read. (It is produced after).
P.P.S. If there are libraries and/or code references I could use as a guide to my task, please share them.
Quick and dirty:
public static Producer<String> readFile(File file, Charset charset) {
Stream<String> stream;
try {
stream = Files.lines(file.toPath(), charset);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
Iterator<String> iter = stream.iterator();
return new Producer<String>() {
#Override
public boolean hasNext() {
if (!iter.hasNext()) {
stream.close();
return false;
}
return true;
}
#Override
public String next() {
return iter.next();
}
};
}
Related
I have a class with several tests, which if run alone pass. Instead, if I run the entire test class, only one passes.
I am testing if the output (ie a String) is equal to an expected String. The output is produced by a method, which requires user keyboard input. In each test, I mock the user input with the following class:
import java.io.*;
import java.nio.charset.Charset;
public class MockInOut {
private PrintStream orig;
private InputStream irig;
private ByteArrayOutputStream os;
private ByteArrayInputStream is;
private final static Charset charset = Charset.defaultCharset();
public MockInOut(String input) {
orig = System.out;
irig = System.in;
os = new ByteArrayOutputStream();
try {
System.setOut(new PrintStream(os, false, charset.name()));
} catch (UnsupportedEncodingException ex) {
throw new RuntimeException(ex);
}
is = new ByteArrayInputStream(input.getBytes());
System.setIn(is);
}
/**
* Returns everything written to System.out since this {#code MockInOut} was
* constructed. Can't be called on a closed {#code MockInOut}
*/
public String getOutput() {
if (os != null) {
try {
return os.toString(charset.name()).replace("\r\n", "\n");
} catch (UnsupportedEncodingException ex) {
throw new RuntimeException(ex);
}
} else {
throw new Error("getOutput on closed MockInOut!");
}
}
/**
* Restores System.in and System.out
*/
public void close() {
os = null;
is = null;
System.setOut(orig);
System.setIn(irig);
}
}
Each test is structured as follows:
#Test
MyClassForConstructor myclassfc = new MyClassForConstructor();
MyClass myclass = new MyClass(myclassfc);
MockInOut userkeyboardinput = new MockInOut("firstLineInput\nsecondLineInput\nthirdLineInput\n");
MyObject myobToGetTested = myclass.methodThatRequiresInput();
MyObject myobExpected = new MyObject(String par1, String par2, String par3);
assertEquals(myobExpected.toString(), myobToGetTested.toString());
userkeyboardinput.close();
Each test has the same structure and what it changes is the keyboard input and the parameters to create the expected object.
Every test throws a NoSuchElementException at Scanner.throwFor() because needInput = false, at Scanner.next(), except for one test (always the same, but which has no explicit difference with the others: in the test that passes, I saw that needInput is set true, but I don't know the reason).
I really don't know what else could be done, thanks for everybody who will help!
Let's assume that I have a java program that creates a report by multiple threads writing .to a file:
public File report = new File("C:\somewhere\file")
public FileWriter fileWriter = new FileWriter("C:\somewhere\file");
//Some thread executed the following statement
fileWriter.write("creating report for this thread");
Instead of using a file, I want to use some type of String buffer to create the report so I can return it in a rest response. What can I use that has the same outcome as if using a File.
Update: I want to completely omit the file implementation as I can't store it in cloud.
You can use the outputstream of the HttpServletResponse to send the file as stream. Don't forget to make your header relevant. You can write a method to process the output as file:
public static void writeFileToOutputStream(HttpServletResponse response, File file) {
String type = "application/octet-stream";
response.setContentType(type);
response.setHeader("Content-Disposition", String.format("inline;filename=\"" + file.getName() + "\""));
response.setContentLength((int) file.length());
InputStream inputStream = null;
try {
inputStream = new BufferedInputStream(new FileInputStream(file));
FileCopyUtils.copy(inputStream, response.getOutputStream());
} catch (IOException e) {
log.info("------couldn't write file------");
}
}
Several threads writing to the same would have one obvious solution: use java.util.logging. Writing to a log file. The content of a log file can also easily be returned as a REST response.
Using a string buffer, StringBuilder is faster, but not thread-safe. The older StringBuffer is thread-safe but not with twice appending, like in:
sb.append("The size is ").append(size); // Not thread-safe.
You could do:
private final StringBuilder sb = new StringBuilder(4096);
public void printf(String messageFormat, Object... args) {
String s = new MessageFormat(....);
synchronized(sb) {
sb.append(s);
}
}
public String extract() {
String s;
synchronized(sb) {
s = sb.toString();
sb.setLength(0);
}
return s;
}
If you want to stay implementation agnostic then you should design to an interface. I'd suggest just plain old Writer. You could have something like:
public abstract class AbstractReportWriter {
protected Writer writer;
public AbstractWriter(Writer w) {
writer = w;
}
public void write(String text) {
writer.write(text);
}
}
public class FileReportWriter extends AbstractReportWriter {
public FileReportWriter(String path) {
super(new FileWriter(path))
}
}
public class StringReportWriter extends AbstractReportWriter {
public StringReportWriter() {
super(new StringWriter())
}
public String getValue() {
return ((StringWriter) writer).toString()
}
}
public class CloudReportWriter extends AbstractReportWriter {
public CloudReportWriter() {
super(new YourCloudWriterClass());
}
}
Then you can pick and choose your writer by just swapping the implementation.
I created a multi page editor with a text editor page and a Master Details Block page. In the Master Details block I have a list with entries which have been parsed from a text file.
These text files have the following structure:
myVar = 23423423;
SEARCH(myVar)
block
{
text = test;
}
When I click on one of the entries, for example myVar, a detail block with two forms will be displayed, one input field for the variable name and one for the corresponding value. If I change the entries through this form, the respective object will be modified. However, it will not mark the multi page editor as dirty and it will not ask me if I want to save my changes. greg-449 told me today, that I need to save the file manually, so I tried the following:
public class MyParser {
private MyModel model;
private long lastModified;
private IFile file;
public MyParser(FileEditorInput input) {
this.file = input.getFile();
this.lastModified = input.getFile().getModificationStamp();
parse();
}
private void parse() {
try {
InputStream in = file.getContents();
BufferedReader reader = new BufferedReader(
new InputStreamReader(in));
StringBuilder out = new StringBuilder();
String line;
List<MyField> variables = new ArrayList<MyField>();
while ((line = reader.readLine()) != null) {
String[] splittedLine = line.split("=");
if (splittedLine.length >= 2)
variables
.add(new MyVariable(splittedLine[0], splittedLine[1]));
// Implement other entry types
}
System.out.println(out.toString()); // Prints the string content
// read from input stream
reader.close();
this.setModel(new MyModel().add(variables, false));
} catch (CoreException | IOException ex) {
}
}
public void saveChanges() {
FileWriter writer = null;
try {
writer = new FileWriter(file.getLocation().toFile());
try {
saveChanges(writer);
} finally {
writer.close();
}
if (file != null) {
try {
file.refreshLocal(IResource.DEPTH_INFINITE,
new NullProgressMonitor());
} catch (CoreException e) {
}
}
} catch (IOException ex) {
}
}
public void saveChanges(Writer writer) {
//TODO
this.getModel().setDirty(false);
}
public long getLastModified() {
return this.lastModified;
}
public MyModel getModel() {
return model;
}
public void setModel(MyModel model) {
this.model = model;
}
How can I save the modified objects to the original file? Do I need to overwrite the file completely or is it possible to change only the dirty values? Furthermore, the order of the entries in the text files is important. Do I need to remember the line numbers, because this may be a problem when adding new entries somewhere in the middle of the file.
Any help is greatly appreciated.
You have to replace the entire contents of the file. Use the IFile.setContents method to set the contents of an existing file. It is up to you to get the order of the contents correct.
For a FormEditor call editorDirtyStateChanged() to tell the editor that the 'dirty' state has changed. Each IFormPage should implement isDirty as appropriate (or override the FormEditor isDirty method).
If the dirty state is correct a '*' is added to the editor title. A 'do you want' to save dialog will also be displayed on exit if the editor is dirty.
I have this InputStream:
InputStream inputStream = new ByteArrayInputStream(myString.getBytes(StandardCharsets.UTF_8));
How can I convert this to ServletInputStream?
I have tried:
ServletInputStream servletInputStream = (ServletInputStream) inputStream;
but do not work.
EDIT:
My method is this:
private static class LowerCaseRequest extends HttpServletRequestWrapper {
public LowerCaseRequest(final HttpServletRequest request) throws IOException, ServletException {
super(request);
}
#Override
public ServletInputStream getInputStream() throws IOException {
ServletInputStream servletInputStream;
StringBuilder jb = new StringBuilder();
String line;
String toLowerCase = "";
BufferedReader reader = new BufferedReader(new InputStreamReader(super.getInputStream()));
while ((line = reader.readLine()) != null) {
toLowerCase = jb.append(line).toString().toLowerCase();
}
InputStream inputStream = new ByteArrayInputStream(toLowerCase.getBytes(StandardCharsets.UTF_8));
servletInputStream = (ServletInputStream) inputStream;
return servletInputStream;
}
}
I´m trying to convert all my request to lowercase.
My advice: don't create the ByteArrayInputStream, just use the byte array you got from the getBytes method already. This should be enough to create a ServletInputStream.
Most basic solution
Unfortunately, aksappy's answer only overrides the read method. While this may be enough in Servlet API 3.0 and below, in the later versions of Servlet API there are three more methods you have to implement.
Here is my implementation of the class, although with it becoming quite long (due to the new methods introduced in Servlet API 3.1), you might want to think about factoring it out into a nested or even top-level class.
final byte[] myBytes = myString.getBytes("UTF-8");
ServletInputStream servletInputStream = new ServletInputStream() {
private int lastIndexRetrieved = -1;
private ReadListener readListener = null;
#Override
public boolean isFinished() {
return (lastIndexRetrieved == myBytes.length-1);
}
#Override
public boolean isReady() {
// This implementation will never block
// We also never need to call the readListener from this method, as this method will never return false
return isFinished();
}
#Override
public void setReadListener(ReadListener readListener) {
this.readListener = readListener;
if (!isFinished()) {
try {
readListener.onDataAvailable();
} catch (IOException e) {
readListener.onError(e);
}
} else {
try {
readListener.onAllDataRead();
} catch (IOException e) {
readListener.onError(e);
}
}
}
#Override
public int read() throws IOException {
int i;
if (!isFinished()) {
i = myBytes[lastIndexRetrieved+1];
lastIndexRetrieved++;
if (isFinished() && (readListener != null)) {
try {
readListener.onAllDataRead();
} catch (IOException ex) {
readListener.onError(ex);
throw ex;
}
}
return i;
} else {
return -1;
}
}
};
Adding expected methods
Depending on your requirements, you may also want to override other methods. As romfret pointed out, it's advisable to override some methods, such as close and available. If you don't implement them, the stream will always report that there are 0 bytes available to be read, and the close method will do nothing to affect the state of the stream. You can probably get away without overriding skip, as the default implementation will just call read a number of times.
#Override
public int available() throws IOException {
return (myBytes.length-lastIndexRetrieved-1);
}
#Override
public void close() throws IOException {
lastIndexRetrieved = myBytes.length-1;
}
Writing a better close method
Unfortunately, due to the nature of an anonymous class, it's going to be difficult for you to write an effective close method because as long as one instance of the stream has not been garbage-collected by Java, it maintains a reference to the byte array, even if the stream has been closed.
However, if you factor out the class into a nested or top-level class (or even an anonymous class with a constructor which you call from the line in which it is defined), the myBytes can be a non-final field rather than a final local variable, and you can add a line like:
myBytes = null;
to your close method, which will allow Java to free memory taken up by the byte array.
Of course, this will require you to write a constructor, such as:
private byte[] myBytes;
public StringServletInputStream(String str) {
try {
myBytes = str.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException("JVM did not support UTF-8", e);
}
}
Mark and Reset
You may also want to override mark, markSupported and reset if you want to support mark/reset. I am not sure if they are ever actually called by your container though.
private int readLimit = -1;
private int markedPosition = -1;
#Override
public boolean markSupported() {
return true;
}
#Override
public synchronized void mark(int readLimit) {
this.readLimit = readLimit;
this.markedPosition = lastIndexRetrieved;
}
#Override
public synchronized void reset() throws IOException {
if (markedPosition == -1) {
throw new IOException("No mark found");
} else {
lastIndexRetrieved = markedPosition;
readLimit = -1;
}
}
// Replacement of earlier read method to cope with readLimit
#Override
public int read() throws IOException {
int i;
if (!isFinished()) {
i = myBytes[lastIndexRetrieved+1];
lastIndexRetrieved++;
if (isFinished() && (readListener != null)) {
try {
readListener.onAllDataRead();
} catch (IOException ex) {
readListener.onError(ex);
throw ex;
}
readLimit = -1;
}
if (readLimit != -1) {
if ((lastIndexRetrieved - markedPosition) > readLimit) {
// This part is actually not necessary in our implementation
// as we are not storing any data. However we need to respect
// the contract.
markedPosition = -1;
readLimit = -1;
}
}
return i;
} else {
return -1;
}
}
Try this code.
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(myString.getBytes(StandardCharsets.UTF_8));
ServletInputStream servletInputStream=new ServletInputStream(){
public int read() throws IOException {
return byteArrayInputStream.read();
}
}
You can only cast something like this:
ServletInputStream servletInputStream = (ServletInputStream) inputStream;
if the inputStream you are trying to cast is actually a ServletInputStream already. It will complain if it's some other implementation of InputStream. You can't cast an object to something it isn't.
In a Servlet container, you can get a ServletInputStream from a ServletRequest:
ServletInputStream servletInputStream = request.getInputStream();
So, what are you actually trying to do?
EDIT
I'm intrigued as to why you want to convert your request to lower-case - why not just make your servlet case-insensitive? In other words, your code to lower-case the request data can be copied into your servlet, then it can process it there... always look for the simplest solution!
Is there a Reader class (JDK or library) I can use to decorate another Reader in such a way that the new reader returns "PREFIX" + everythong of innerReader + "POSTFIX"?
I want to decorate the file contents with a header and a footer before returning the Reader to the caller.
Not in the standard library, but take a look at http://ostermiller.org/utils/Concat.html
Looks promising, but I haven't used it myself.
I've built this on behalf of GreyBeardedGeek's post, maybe somebody can use it:
/**
* Utility <code>Reader</code> implementation which joins one or more other <code>Reader</code> to appear as one.
*/
public class CompositeReader extends Reader {
/** Logger. */
private final static Logger log = LoggerFactory.getLogger(CompositeReader.class);
/** List of readers (in order). */
private final Reader[] readers;
/** Current index. */
private int index;
/**
* #param readers ordered list of <code>Reader</code> to read from.
*/
public CompositeReader(final Reader... readers) {
checkArgument(readers.length > 0, "Argument readers must not be empty.");
this.readers = readers;
index = 0;
}
#Override
public int read(final char[] cbuf, final int off, final int len) throws IOException {
int read = 0;
while (read < len && index != readers.length) {
final Reader reader = readers[index];
final int readFromReader = reader.read(cbuf, off + read, len - read);
if (readFromReader == -1) {
++index;
} else {
read += readFromReader;
}
}
if (read == 0) {
return -1;
}
return read;
}
#Override
public void close() throws IOException {
IOException firstException = null;
for (final Reader reader : readers) {
try {
reader.close();
} catch (final IOException ex) {
if (firstException != null) {
log.warn("Multiple readers could not be closed, only first exception will be thrown.");
firstException = ex;
}
}
}
if (firstException != null) {
throw firstException;
}
}
}
Here you go :-)