Is it possible to force Properties not to add the date comment in front? I mean something like the first line here:
#Thu May 26 09:43:52 CEST 2011
main=pkg.ClientMain
args=myargs
I would like to get rid of it altogether. I need my config files to be diff-identical unless there is a meaningful change.
Guess not. This timestamp is printed in private method on Properties and there is no property to control that behaviour.
Only idea that comes to my mind: subclass Properties, overwrite store and copy/paste the content of the store0 method so that the date comment will not be printed.
Or - provide a custom BufferedWriter that prints all but the first line (which will fail if you add real comments, because custom comments are printed before the timestamp...)
Given the source code or Properties, no, it's not possible. BTW, since Properties is in fact a hash table and since its keys are thus not sorted, you can't rely on the properties to be always in the same order anyway.
I would use a custom algorithm to store the properties if I had this requirement. Use the source code of Properties as a starter.
Based on https://stackoverflow.com/a/6184414/242042 here is the implementation I have written that strips out the first line and sorts the keys.
public class CleanProperties extends Properties {
private static class StripFirstLineStream extends FilterOutputStream {
private boolean firstlineseen = false;
public StripFirstLineStream(final OutputStream out) {
super(out);
}
#Override
public void write(final int b) throws IOException {
if (firstlineseen) {
super.write(b);
} else if (b == '\n') {
firstlineseen = true;
}
}
}
private static final long serialVersionUID = 7567765340218227372L;
#Override
public synchronized Enumeration<Object> keys() {
return Collections.enumeration(new TreeSet<>(super.keySet()));
}
#Override
public void store(final OutputStream out, final String comments) throws IOException {
super.store(new StripFirstLineStream(out), null);
}
}
Cleaning looks like this
final Properties props = new CleanProperties();
try (final Reader inStream = Files.newBufferedReader(file, Charset.forName("ISO-8859-1"))) {
props.load(inStream);
} catch (final MalformedInputException mie) {
throw new IOException("Malformed on " + file, mie);
}
if (props.isEmpty()) {
Files.delete(file);
return;
}
try (final OutputStream os = Files.newOutputStream(file)) {
props.store(os, "");
}
if you try to modify in the give xxx.conf file it will be useful.
The write method used to skip the First line (#Thu May 26 09:43:52 CEST 2011) in the store method. The write method run till the end of the first line. after it will run normally.
public class CleanProperties extends Properties {
private static class StripFirstLineStream extends FilterOutputStream {
private boolean firstlineseen = false;
public StripFirstLineStream(final OutputStream out) {
super(out);
}
#Override
public void write(final int b) throws IOException {
if (firstlineseen) {
super.write(b);
} else if (b == '\n') {
// Used to go to next line if did use this line
// you will get the continues output from the give file
super.write('\n');
firstlineseen = true;
}
}
}
private static final long serialVersionUID = 7567765340218227372L;
#Override
public synchronized Enumeration<java.lang.Object> keys() {
return Collections.enumeration(new TreeSet<>(super.keySet()));
}
#Override
public void store(final OutputStream out, final String comments)
throws IOException {
super.store(new StripFirstLineStream(out), null);
}
}
Can you not just flag up in your application somewhere when a meaningful configuration change takes place and only write the file if that is set?
You might want to look into Commons Configuration which has a bit more flexibility when it comes to writing and reading things like properties files. In particular, it has methods which attempt to write the exact same properties file (including spacing, comments etc) as the existing properties file.
You can handle this question by following this Stack Overflow post to retain order:
Write in a standard order:
How can I write Java properties in a defined order?
Then write the properties to a string and remove the comments as needed. Finally write to a file.
ByteArrayOutputStream baos = new ByteArrayOutputStream();
properties.store(baos,null);
String propertiesData = baos.toString(StandardCharsets.UTF_8.name());
propertiesData = propertiesData.replaceAll("^#.*(\r|\n)+",""); // remove all comments
FileUtils.writeStringToFile(fileTarget,propertiesData,StandardCharsets.UTF_8);
// you may want to validate the file is readable by reloading and doing tests to validate the expected number of keys matches
InputStream is = new FileInputStream(fileTarget);
Properties testResult = new Properties();
testResult.load(is);
Related
I'm essentially asking the same as this old question, but for Java 14 instead of Java 8. To spare answerers the trouble of navigating to the old question, I'll rephrase it here.
I want to get the name of a function from a referenced method. The following Java code should give you the idea:
public class Main
{
public static void main(String[] args)
{
printMethodName(Main::main);
}
private static void printMethodName(Consumer<String[]> theFunc)
{
String funcName = // somehow get name from theFunc
System.out.println(funcName)
}
}
The equivalent in C# would be:
public class Main
{
public static void Main()
{
var method = Main.Main;
PrintMethodName(method)
}
private static void PrintMethodName(Action action)
{
Console.WriteLine(action.GetMethodInfo().Name);
}
}
According to the accepted answer of the old question, this was not possible in Java 8 without considerable work, such as this solution. Is there a more elegant solution in Java 14?
Getting a method info from a method reference never was a goal on the JDK developer’s side, so no effort was made to change the situation.
However, the approach shown in your link can be simplified. Instead of serializing the information, patching the serialized data, and restoring the information using a replacement object, you can simply intercept the original SerializedLambda object while serializing.
E.g.
public class GetSerializedLambda extends ObjectOutputStream {
public static void main(String[] args) { // example case
var lambda = (Consumer<String[]>&Serializable)GetSerializedLambda::main;
SerializedLambda sl = GetSerializedLambda.get(lambda);
System.out.println(sl.getImplClass() + " " + sl.getImplMethodName());
}
private SerializedLambda info;
GetSerializedLambda() throws IOException {
super(OutputStream.nullOutputStream());
super.enableReplaceObject(true);
}
#Override protected Object replaceObject(Object obj) throws IOException {
if(obj instanceof SerializedLambda) {
info = (SerializedLambda)obj;
obj = null;
}
return obj;
}
public static SerializedLambda get(Object obj) {
try {
GetSerializedLambda getter = new GetSerializedLambda();
getter.writeObject(obj);
return getter.info;
} catch(IOException ex) {
throw new IllegalArgumentException("not a serializable lambda", ex);
}
}
}
which will print GetSerializedLambda main. The only newer feature used here, is the OutputStream.nullOutputStream() to drop the written information immediately. Prior to JDK 11, you could write into a ByteArrayOutputStream and drop the information after the operation which is only slightly less efficient. The example also using var, but this is irrelevant to the actual operation of getting the method information.
The limitations are the same as in JDK 8. It requires a serializable method reference. Further, there is no guaranty that the implementation will map to a method directly. E.g., if you change the example’s declaration to public static void main(String... args), it will print something like lambda$1 when being compiled with Eclipse. When also changing the next line to var lambda = (Consumer<String>&Serializable)GetSerializedLambda::main;, the code will always print a synthetic method name, as using a helper method is unavoidable. But in case of javac, the name is rather something like lambda$main$f23f6912$1 instead of Eclipse’s lambda$1.
In other words, you can expect encountering surprising implementation details. Do not write applications relying on the availability of such information.
I have some theoretical knowledge related to Design Patterns and now I have some issues to make these info and another ones in action.
I have the following 2 methods in my ServiceImpl class:
#Override
public MultipartFile exportA() throws IOException {
// repeated lines-I same as exportB method (code omitted for brevity)
// other lines special to exportA method
// repeated lines-II same as exportB method (code omitted for brevity)
}
#Override
public MultipartFile exportB() throws IOException {
// repeated lines-I same as exportA method (code omitted for brevity)
// other lines special to exportB method
// repeated lines-II same as exportA method (code omitted for brevity)
}
As it is shown, there are repeated parts in all of these methods. So, should I create 2 methods for repeated lines-I and II, eand then move these code blocks to these newly created 2 methods? Or, is there a better way for Design Patterns?
If I have well understood your statement, this sounds to me a
Builder Pattern that you are looking for. You methods are building a MultipartFile whereas the build process itself depends on 'arguments/parameters' (here I guess sheet file path) and distinct code (the one you referred to as "other lines special to this method").
For that I would create a class MultipartFileBuilder that does the staff and that I would call in each method; of course, by means of setting the appropriate parameters and "code" each time. The code is simply an implementation of the java.util.function.Consumer<T> functional interface used in the following code (*2) and other parameters are using simple setters as well (here (*1)).
Note that I invoked the Consumer<T> as lambda expression here (the c->... construct). And note also that the type parameter <T> in Consumer<T> here is a new class I introduced MultipartFileBuildContext to allow multiple information to be passed to the code you are willing to write in each method. I guest the 'sheet' var would be a starting point. You can add other information if you need to.
To summer up things, this is how the code would look :
#Override
public MultipartFile exportMethodA() throws IOException {
return new MultipartFileBuilder()
.setSheetPath("sheetA/path") (*1)
.setAction(c->{
//(*2) do your staff here for method A
// the "other lines special to this method"
XSSFSheet sheet=c.getSheet()
...
}).build();
}
#Override
public MultipartFile exportMethodB() throws IOException {
return new MultipartFileBuilder()
.setSheetPath("sheetB/path") (*1)
.setAction(c->{
//(*2) do your staff here for method B
// the "other lines special to this method"
XSSFSheet sheet=c.getSheet()
...
}).build();
}
class MultipartFileBuildContext {
private XSSFSheet sheet;
public MultipartFileBuildContext(XSSFSheet sheet){this.sheet=sheet;}
public String getSheetPath() {
return sheetPath;
}
}
class MultipartFileBuilder {
String sheetPath;
Consumer<MultipartFileBuildContext> action;
public String getSheetPath() {
return sheetPath;
}
public MultipartFileBuilder setSheetPath(String sheetPath) {
this.sheetPath = sheetPath;
return this;
}
public Consumer<MultipartFileBuildContext> getAction() {
return action;
}
public MultipartFileBuilder setAction(Consumer<MultipartFileBuildContext> action) {
this.action = action;
return this;
}
public MockMultipartFile build() {
// repeated lines
workbook = new XSSFWorkbook();
sheet = workbook.createSheet(sheetPath);
//
if(action!=null){
MultipartFileBuildContext c=new MultipartFileBuildContext(sheet);
action.accept(c);
}
// repeated lines ======================================================
outputFile = File.createTempFile(...);
try (FileOutputStream outputStream = new FileOutputStream(outputFile)) {
workbook.write(outputStream);
} catch (IOException e) {
LoggingUtils.error("Writing failed ", e);
}
final FileInputStream input = new FileInputStream(outputFile);
final String fileName = TextBundleUtil.read(...);
return new MockMultipartFile(fileName,
fileName, CONTENT_TYPE, IOUtils.toByteArray(input));
//
}
}
At the end, this pattern needs to be used with care because you need to factorize all that you can to make the builder really useful, but not too much to make it a "boat of anything". For instance, you can have as input the sheet path or an inputstream of it to make it more useful/generic.
I have a code segment where a new child process is created and some of the new process operation results are required to be sent to the parent process from child. Therefore I create a new ObjectOutputStream to the standard output from child class ObjectOutputStream stream = new ObjectOutputStream(System.out); and serialize the objects from the child process and send to the parent and the de-serialization is done within the parent.
It works fine with no issues. But the problem comes when I try to use System.out.println() in the child code where it is also writing to the standard output. Parent process try to de-serialize System.out.println() also and then there will be exceptions in the parent.
Classes are detailed below
public class DTO implements Serializable{
private static final long serialVersionUID = 1L;
private String name;
public DTO()
{
this.name = "name";
}
public String getName() {
return name;
}
#Override
public int hashCode() {}
#Override
public boolean equals(Object obj) {}
Parent.java
public class Parent {
public static void main(String[] args) {
try {
new Parent().start();
} catch (Exception e) {
e.printStackTrace();
}
}
public void start() throws IOException, InterruptedException, ClassNotFoundException
{
String classpath = System.getProperty("java.class.path");
String className = Child.class.getCanonicalName();
ProcessBuilder builder = new ProcessBuilder(
"java", "-cp", classpath, className);
Process process = builder.start();
if (process.isAlive()) {
ObjectInputStream input = new ObjectInputStream(process.getInputStream());
DTO dto = (DTO)input.readObject();
}
}
}
Child.java
public class Child {
public static void main(String[] args) throws IOException {
DTO dto = new DTO();
System.out.println("printing random text here");
ObjectOutputStream stream = new ObjectOutputStream(System.out);
stream.writeObject(dto);
stream.flush();
stream.close();
}
}
Exception
java.io.StreamCorruptedException: invalid stream header: 64617364
at java.io.ObjectInputStream.readStreamHeader(Unknown Source)
at java.io.ObjectInputStream.<init>(Unknown Source)
at working.Parent.start(Parent.java:35)
at working.Parent.main(Parent.java:14)
please note that if we remove the System.out.println() used in the child.java, the program will execute without errors. As I think this happens because both the serialization and the sysout are writing to the standard output, the parent thinks it can de-serialize both. Any suggestion to fix this or a different type of approach to do this would be appreciated
You corrupted the stream, by writing a string to it directly. So you got a StreamCorruptedException. No surprise there. You can't do that. You can't deserialize an object stream that also contains random interpolated System.out.println()s. This should be obvious.
Any suggestion to fix this
Yes. Don't do it.
or alternative approach
Alternative approach to what?
You simply have to decide what you're using System.out for. Either it is an object output stream or it is used to print text to. Not both at the same time.
NB You have still not put in any code to read the errror stream as I told you last time you posted this code. You can't deploy code without error checking.
I defined a job flow in my batch Spring project and defined ItemReader, ItemProcessor, ItemWriter, etc.
My ItemReader as below code :
#Component
#StepScope
public class MyFileReader extends FlatFileItemReader<FileInfo> {
private String fileName;
public MyFileReader () {
}
#Value("#{jobParameters[fileName]}")
public void setFileName(final String fileName) {
this.fileName = fileName;
}
#Override
public void afterPropertiesSet() throws Exception {
Resource resource = new FileSystemResource(fileName);
setResource(resource);
setEncoding("UTF-8");
super.afterPropertiesSet();
}
}
and my file input format is:
111111,11111,111,111
222222,22222,222,222
I want to read all lines of file and return lines and file address to ItemProcessor, but FlatFileItemReader read line by line. How do I do it correctly? Is overriding doRead method and handle problem manually correct?
If I'm understanding the question, you want to read in all lines from a file, store that data in an object and then pass said object to the processor. One approach would be to read all lines from the file before the job starts using a Job Listener. As illustrated below, you could read all lines in, populate a Java object that represents the content of a single row, collect all of those objects (so if there were two rows you'd populate 2 beans), and then pass them to the processor one at a time (or potentially at the same time, if you wish). It would look something like this:
First you would create a listener.
public class MyJobListenerImpl implements JobExecutionListener {
private MyFileReader reader;
#Override
public void beforeJob(JobExecution jobExecution) {
reader.init();
}
#Override
public void afterJob(JobExecution jobExecution) {
// noop
}
// Injected
public void setReader(MyFileReader reader) {
this.reader = reader;
}
Next add an init method to your custom reader.
public void init() {
if(Files.exists(inputFileLocation)) {
List<String> inputData = null;
try {
inputData = Files.readAllLines(inputFileLocation, StandardCharsets.UTF_8);
} catch(IOException e) {
System.out.println("issue reading input file {}. Error message: {}", inputFileLocation, e);
throw new IllegalStateException("could not read the input file.");
}
try {
for(String fileItem : inputData) {
YourFileDataBean fileData = new YourFileDataBean();
yourFileDataBean.setField1(fileItem.split(",")[0].trim());
yourFileDataBean.setFiled2(fileItem.split(",")[1].trim());
yourFileDataBean.setField3(fileItem.split(",")[2].trim());
yourFileDataBean.setField4(fileItem.split(",")[3].trim());
myDeque.add(yourFileDataBean); // really up to you how you want to store your bean but you could add a Deque instance variable and store it there.
}
} catch(ArrayIndexOutOfBoundsException e) {
LOGGER.warn("ArrayIndexOutOfBoundsException due to data in input file.");
throw new IllegalStateException("Failure caused by init() method. Error reading in input file.");
}
} else {
LOGGER.warn("Input file {} does not exist.", inputFileLocation);
throw new IllegalStateException("Input file does not exist at file location " + inputFileLocation);
}
}
Make your read() (or MyFileReader()) method in your custom reader return the object populated by all the file lines read in. In this example I am implementing ItemReader rather than extending it as you have done, but you get the idea. And if you intend to return a single Java object that represents the entire file then there would be no need to store the object in a Deque or List.
#Override
public MyFileReader read() throws NonTransientResourceException {
return myDeque.poll();
}
Hope this helps.
As for returning the file address to the ItemProcessor. You could make this a field in YourFileDataBean and store inputFileLocation there, or save it to the execution context and access it that way. If you inject this file path into your reader, you could do the same in your processor assuming your reader plays no role in determining the file path (aka, it's predetermined).
I am working on this project where the user enters some data which is written to an XML file.This part is working fine.
Now when the user runs the program he should be able to append to that file. Instead it creates a new file with just one entry!
A fileoutput stream is also not the solution.
Here is the code for serializing to XML
String medicine=medicfield.getText();
String doctor=dnamefield.getText();
int duration=Integer.parseInt(dodfield.getText());
int amount=Integer.parseInt(cyclefield.getText());
int inter=Integer.parseInt(intval.getText());
PrescripManager pm=new PrescripManager();
pm.setDcycle(amount);
pm.setDosage(duration);
pm.setInterval(inter);
pm.setmedName(medicine);
pm.setdocName(doctor);
try{
FileOutputStream file = new FileOutputStream("file.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(PrescripManager.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
// output pretty printed
jaxbMarshaller.marshal(pm, file);
}
catch(Exception ex)
{
erlbl.setText(ex.getMessage());
}
And the Class::
#XmlRootElement
public class PrescripManager {
private String medname,docname;
private int interval,dcycle,dosage;
private Date dt;
public String getmedName() {
return medname;
}
public void setmedName(String medname) {
this.medname = medname;
}
public String getdocName() {
return docname;
}
public void setdocName(String docname) {
this.docname = docname;
}
public int getInterval() {
return interval;
}
public void setInterval(int interval) {
this.interval = interval;
}
public int getDcycle() {
return dcycle;
}
public void setDcycle(int dcycle) {
this.dcycle = dcycle;
}
public int getDosage() {
return dosage;
}
public void setDosage(int dosage) {
this.dosage = dosage;
}
}
First of all, you are writing an XML file. You can not just append to an XML file, because that would mean you are writing after then closing top level tag, resulting in invalid XML file.
You have at least three choices:
read old file in, add to the actual data, then write entire XML-file back.
write multiple files, each a valid XML file, with sequence number or timestamp in file name.
do not use XML, use a format which can be appended to
As a side note, if you want to append to file, you can open it in append mode. That will make every write to it append (at least on Unix, when file is opened in append mode, and I presume it works the same in Windows).
How to open file in append mode in Java: http://docs.oracle.com/javase/6/docs/api/java/io/FileOutputStream.html#FileOutputStream(java.io.File, boolean)
You can use the "FileWriter" class which allows you to write at the end of a file. See
http://docs.oracle.com/javase/6/docs/api/java/io/FileWriter.html
This link should help you:
http://www.coderanch.com/t/276346//java/do-open-file-append-mode
I think it should work if you use
FilterWriter file = new FileWriter( "file.xml" , true );
instead of
FileOutputStream file = new FileOutputStream("file.xml");
You are using JAXB to process XML files, so it's better that you change your XML file format and Java class to support this.
You can add a new class as collections of PrescripManager class instances. Something like PrescripManagerList.
public class PrescripManagerList{
#XmlElementWrapper(name = "prescripManagers")
#XmlElement(name = "prescripManager")
private ArrayList<PrescripManager> prescripManagers;
}
When you running your code, try to read an existing XML file using JAXB unmarshaller to get a PrescripManagerList object, then add a new PrescripManager object to the ArrayList, then write the updated PrescripManagerList object to file using JAXB marshaller.