/*
* ALMA - Atacama Large Millimiter Array
* (c) European Southern Observatory, 2002
* Copyright by ESO (in the framework of the ALMA collaboration)
* and Cosylab 2002, All rights reserved
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
package alma.acs.logging.tools;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.FieldPosition;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.cosylab.logging.engine.ACS.ACSRemoteErrorListener;
import com.cosylab.logging.engine.ACS.ACSRemoteLogListener;
import com.cosylab.logging.engine.log.ILogEntry;
import com.cosylab.logging.engine.log.LogField;
import alma.acs.logging.engine.io.IOHelper;
import alma.acs.logging.engine.io.IOPorgressListener;
import alma.acs.logging.engine.parser.ACSLogParser;
import alma.acs.logging.engine.parser.ACSLogParserFactory;
import alma.acs.util.IsoDateFormat;
/**
* An object to split a log file in several other shorter file.
* The criteria is the number of logs or the time (in minutes)
*
* A progressive number is appended to the name of the destination
* for each created file.
* If the time is the criteria to split the input file, the
* the date of the first log is appended to the name of the file too.
*
* @author acaproni
*
*/
public class LogFileSplitter implements ACSRemoteLogListener, ACSRemoteErrorListener, IOPorgressListener {
/**
* The name of the input files
*/
private final String[] inFileNames;
/**
* The name for the output files
* <P>
* A progressive number/starting date is appended at the end of each name
*/
private String destFileName;
/**
* The progressive number appended at the end of each generated file
*/
private int index=0;
/**
* The number of logs in each splitted file
*/
private Integer number=null;
/**
* The time frame of the logs in each splitted file (in msec)
*/
private Integer time=null;
/**
* Counts the number of logs read (needed for number criteria)
*/
private int logsRead=0;
/**
* The date of the first log in the current output file (needed for time criteria)
*/
private long firstLogDate=-1;
/**
* The size of the buffer for writing
*/
private static final int OUTPUT_BUFFER_SIZE=8192;
/**
* The writer to write the destination files
*/
private BufferedWriter outF=null;
/**
* The format of the date in the name of the file
*/
private final SimpleDateFormat dateFormat = new IsoDateFormat();
/**
* The converter to format the log before saving
*/
private final LogConverter converter;
/**
* Constructor
*
* @param inputFiles The files of log to read
* @param outputFiles The names of the files created splitting
* @param num The number of logs per file (can be null)
* @param mins The minutes of the logs per file (can be null)
* @param converter The converter to format the logs before saving
*/
public LogFileSplitter(
String[] inputFiles,
String outputFiles,
Integer num,
Integer mins,
LogConverter converter) {
if (outputFiles==null) {
throw new IllegalArgumentException("The dest file name can't be null");
}
if (inputFiles!=null && inputFiles.length==0) {
throw new IllegalArgumentException("No source files");
}
if (num==null && mins==null) {
throw new IllegalArgumentException("Missing criteria (num and time are null)");
}
if (num!=null && mins!=null) {
throw new IllegalArgumentException("Number and time criteria requested");
}
if (num!=null && num<=0) {
throw new IllegalArgumentException("Invalid number "+num);
}
if (mins!=null && mins<=0) {
throw new IllegalArgumentException("Invalid minutes "+num);
}
if (num!=null && num<50000) {
System.out.println("Warning splitting for less then 50000 logs can create a big number of files");
}
if (converter==null) {
throw new IllegalArgumentException("The converter can't be null");
}
this.converter=converter;
inFileNames=new String[inputFiles.length];
System.arraycopy(inputFiles, 0, inFileNames, 0, inputFiles.length);
destFileName=outputFiles;
number=num;
if (mins!=null) {
time=mins*60*1000;
}
}
/**
* Split the input file
* @throws Exception in case of errors while splitting
*/
public void split() throws Exception {
IOHelper ioHelper = new IOHelper();
if (inFileNames==null) {
// Read from stdin
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
ioHelper.loadLogs(in, this, null, this, this);
} else {
for (String inFileName: inFileNames) {
System.out.println("Processing "+inFileName);
ioHelper.loadLogs(inFileName, this, null, this, this);
}
}
if (outF!=null) {
closeOutputFile(outF);
}
}
/**
* Create a new file for output.
*
* @param dest The name of the destination file
* @param index The index to append to the name
* @param startingDate The date of the first log to append to the
* name of the file
* It can be null.
*
* @return The writer for output
*/
private BufferedWriter getOutputFile(String dest, int idx, Date startingDate) throws IOException {
// Build the name of the file
StringBuilder name = new StringBuilder(dest);
name.append('-');
name.append(idx);
if (startingDate!=null) {
name.append('-');
StringBuffer buffer = new StringBuffer();
synchronized (dateFormat) {
dateFormat.format(startingDate,buffer, new FieldPosition(0));
}
name.append(buffer.toString());
}
// Add the extension
if (converter instanceof XMLConverter) {
name.append(".xml");
} else {
name.append(".txt");
}
// Create and return the file
FileWriter outFile=null;
outFile = new FileWriter(name.toString(),false);
System.out.println("Writing logs on "+name);
return new BufferedWriter(outFile,OUTPUT_BUFFER_SIZE);
}
/**
* Flush and close the file
*
* @param file The file to close
*/
private void closeOutputFile(BufferedWriter file) throws Exception {
// Flush and close the old file
if (file!=null) {
file.flush();
file.close();
file=null;
}
}
/**
* @see alma.acs.logging.engine.io.IOPorgressListener#bytesRead(long)
*/
@Override
public void bytesRead(long bytes) {}
/**
* @see alma.acs.logging.engine.io.IOPorgressListener#bytesWritten(long) } catch (IOException e) {
System.err.print("Error closing output file: ");
System.err.print(e.getMessage());
System.exit(-1);
}
*/
@Override
public void bytesWritten(long bytes) {}
/**
* @see alma.acs.logging.engine.io.IOPorgressListener#logsRead(int)
*/
@Override
public void logsRead(int numOfLogs) {}
/**
* @see alma.acs.logging.engine.io.IOPorgressListener#logsWritten(int)
*/
@Override
public void logsWritten(int numOfLogs) {}
/**
* @see com.cosylab.logging.engine.ACS.ACSRemoteErrorListener#errorReceived(java.lang.String)
*/
@Override
public void errorReceived(String xml) {
System.err.println("Error parsing the following: ["+xml+"]");
}
/**
* @see com.cosylab.logging.engine.ACS.ACSRemoteLogListener#logEntryReceived(com.cosylab.logging.engine.log.ILogEntry)
*/
@Override
public void logEntryReceived(ILogEntry logEntry) {
if (number!=null ) {
// Number criteria
if (outF==null || ++logsRead>number) {
try {
closeOutputFile(outF);
} catch (Throwable t) {
System.err.println("Error closing the output file");
t.printStackTrace(System.err);
}
try {
outF=getOutputFile(destFileName,index++,null);
} catch (IOException e) {
System.err.println("Error getting a new file for output: " + e.getMessage());
e.printStackTrace(System.err);
}
logsRead=1;
}
} else {
// Time criteria
long logDate = ((Long)logEntry.getField(LogField.TIMESTAMP));
if (firstLogDate==-1 || logDate-firstLogDate>time) {
firstLogDate=logDate;
try {
closeOutputFile(outF);
} catch (Throwable t) {
System.err.println("Error closing the output file");
t.printStackTrace(System.err);
}
try {
outF=getOutputFile(destFileName,index++,new Date(logDate));
} catch (IOException e) {
System.err.println("Error getting a new file for output: " + e.getMessage());
e.printStackTrace(System.err);
}
}
}
try {
outF.write(converter.convert(logEntry));
} catch (IOException e) {
System.err.println("Error writing a log: " + e.getMessage());
e.printStackTrace(System.err);
}
}
}