/*
* Copyright (C) 2003-2007 eXo Platform SAS.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see<http://www.gnu.org/licenses/>.
*/
package org.exoplatform.services.jcr.impl.core.query;
import org.exoplatform.commons.utils.PrivilegedFileHelper;
import org.exoplatform.commons.utils.SecurityHelper;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/**
* All changes that must be in index but interrupted by IOException are here.
* Created by The eXo Platform SAS Author : Sergey Karpenko
* sergey.karpenko@exoplatform.com.ua
*
* @version $Id: $
*/
public class ErrorLog
{
/**
* Logger instance for this class
*/
private static final Log LOG = ExoLogger.getLogger("exo.jcr.component.core.ErrorLog");
/**
* REMOVE term.
*/
public static final String REMOVE = "rem";
/**
* ADD term.
*/
public static final String ADD = "add";
/**
* The log file
*/
private final File logFile;
/**
* Writer to the log file
*/
private FileChannel out;
/**
* File size in Kb. Used on create and clear(truncate) methods.
*/
private int fileSize = 0; // Kb
/**
* ErrorLog constructor.
*
* @param file
* @param errorLogSize
* @throws IOException
*/
public ErrorLog(File file, int errorLogSize) throws IOException
{
fileSize = errorLogSize;
logFile = file;
openFile(file);
}
/**
* openFile.
*
* @param log
* @throws IOException
*/
private void openFile(final File log) throws IOException
{
SecurityHelper.doPrivilegedIOExceptionAction(new PrivilegedExceptionAction<Void>()
{
@SuppressWarnings("resource")
public Void run() throws Exception
{
// set file size;
if (!log.exists())
{
log.getParentFile().mkdirs();
log.createNewFile();
out = new FileOutputStream(log).getChannel();
out.position(1024 * fileSize - 1);
out.write(ByteBuffer.wrap(new byte[]{0}));
out.position(0);
out.force(false);
}
else
{
out = new FileOutputStream(log, true).getChannel();
}
return null;
}
});
}
/**
* Appends an action to the log.
*
* @param action
* the action to append.
* @throws IOException
* if the node cannot be written to the redo log.
*/
public void append(final String action, final String uuid) throws IOException
{
initOut();
out.write(ByteBuffer.wrap((action + " " + uuid + "\n").getBytes()));
}
/**
* Flushes all pending writes to the underlying file.
*
* @throws IOException
* if an error occurs while writing.
*/
public void flush() throws IOException
{
if (out != null)
{
out.force(false);
}
}
/**
* Clears the redo log.
*
* @throws IOException
* if the redo log cannot be cleared.
*/
public void clear() throws IOException
{
if (out != null)
{
out.truncate(0);
out.close();
out = PrivilegedFileHelper.fileOutputStream(logFile).getChannel();
out.position(1024 * fileSize - 1);
out.write(ByteBuffer.wrap(new byte[]{0}));
out.position(0);
out.force(false);
}
}
/**
* Initializes the {@link #out} stream if it is not yet set.
*
* @throws IOException
* if an error occurs while creating the output stream.
*/
private void initOut() throws IOException
{
if (out == null)
{
FileOutputStream os = PrivilegedFileHelper.fileOutputStream(logFile, false);
out = os.getChannel();
}
}
/**
* Reads the log file .
*
* @throws IOException
* if an error occurs while reading from the log file.
*/
public List<String> readList() throws IOException
{
InputStream in = PrivilegedFileHelper.fileInputStream(logFile);
try
{
List<String> list = new ArrayList<String>();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String line;
while ((line = reader.readLine()) != null)
{
if (!line.matches("\\x00++"))
{
list.add(line);
}
}
return list;
}
finally
{
if (in != null)
{
try
{
in.close();
}
catch (IOException e)
{
LOG.warn("Exception while closing error log: " + e.toString());
}
}
}
}
public void readChanges(Set<String> rem, Set<String> add) throws IOException
{
List<String> list = readList();
Iterator<String> it = list.iterator();
while (it.hasNext())
{
String[] str = it.next().split(" ");
if (str.length == 2)
{
if (str[0].equals(ADD))
{
add.add(str[1]);
}
else if (str[0].equals(REMOVE))
{
rem.add(str[1]);
}
}
}
}
public void writeChanges(Set<String> removed, Set<String> added) throws IOException
{
try
{
if (!removed.isEmpty())
{
Iterator<String> rem = removed.iterator();
while (rem.hasNext())
{
append(ErrorLog.REMOVE, rem.next());
}
}
if (!added.isEmpty())
{
Iterator<String> add = added.iterator();
while (add.hasNext())
{
append(ErrorLog.ADD, add.next());
}
}
}
finally
{
flush();
}
}
/**
* Closes ErrorLog and frees resources associated with it.
*/
public void close()
{
if (out != null)
{
try
{
out.close();
}
catch (IOException e)
{
out = null;
}
}
}
}