All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fr.dyade.aaa.util.JTransaction Maven / Gradle / Ivy

There is a newer version: 5.22.0-EFLUID
Show newest version
/*
 * Copyright (C) 2001 - 2023 ScalAgent Distributed Technologies
 * Copyright (C) 1996 - 2000 BULL
 * Copyright (C) 1996 - 2000 INRIA
 *
 * 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 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 fr.dyade.aaa.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.NotActiveException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.RandomAccessFile;
import java.io.Serializable;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Map;

import org.objectweb.util.monolog.api.BasicLevel;

/**
 *  The JTransaction class implements a transactionnal storage.
 *  This implementation is simple but inefficient, its main advantage is
 * the compatibility with old JDK.
 *
 * @see Transaction
 */
@Deprecated
public final class JTransaction extends BaseTransaction implements JTransactionMBean {
  protected long startTime = 0L;

  /**
   * Returns the starting time.
   *
   * @return The starting time.
   */
  public long getStartTime() {
    return startTime;
  }

  /**
   * Number of commit operation since starting up.
   */
  private int commitCount = 0;

  /**
   * Returns the number of commit operation since starting up.
   *
   * @return The number of commit operation.
   */
  public final int getCommitCount() {
    return commitCount;
  }

  public static final String EMPTY_STRING = new String();

  /**
   *  Number of pooled operation, by default 100.
   *  This value can be adjusted for a particular server by setting
   * LogThresholdOperation specific property.
   * 

* These property can be fixed either from java launching * command, or in a3servers.xml configuration file. */ static int LogThresholdOperation = 100; private File dir = null; static private final String LOG = "log"; private RandomAccessFile logFile = null; private Hashtable log = null; public JTransaction() {} /** * Tests if the Transaction component is persistent. * * @return true. */ public boolean isPersistent() { return true; } public void init(String path) throws IOException { phase = INIT; dir = new File(path); if (!dir.exists()) dir.mkdir(); if (!dir.isDirectory()) throw new FileNotFoundException(path + " is not a directory."); // Saves the transaction classname in order to prevent use of a // different one after restart (see AgentServer.init). DataOutputStream dos = null; try { File tfc = new File(dir, "TFC"); if (! tfc.exists()) { dos = new DataOutputStream(new FileOutputStream(tfc)); dos.writeUTF(getClass().getName()); dos.flush(); } } finally { if (dos != null) dos.close(); } loadProperties(dir); LogThresholdOperation = getInteger("LogThresholdOperation", LogThresholdOperation).intValue(); Operation.initPool(LogThresholdOperation); saveProperties(dir); // Read the log, then... int oldPhase = FREE; logFile = new RandomAccessFile(new File(dir, LOG), "rw"); log = new Hashtable(); if (logFile.length() != 0) { logFile.seek(0L); oldPhase = logFile.readInt(); // Test the stop status then complete commit or rollback if needed if (oldPhase == COMMIT) { int op; String name; while (!(name = logFile.readUTF()).equals("")) { String dirName = logFile.readUTF(); if (dirName.length() == 0) dirName = null; Object key = OperationKey.newKey(dirName, name); op = logFile.read(); if (op == Operation.SAVE) { byte buf[] = new byte[logFile.readInt()]; logFile.readFully(buf); log.put(key, Operation.alloc(Operation.SAVE, dirName, name, buf)); } else { log.put(key, Operation.alloc(op, dirName, name)); } } } _commit(); } startTime = System.currentTimeMillis(); setPhase(FREE); } public File getDir() { return dir; } /** * Returns the path of persistence directory. * * @return The path of persistence directory. */ public String getPersistenceDir() { return dir.getPath(); } // State of the transaction monitor. protected int phase; public final int getPhase() { return phase; } public final String getPhaseInfo() { return PhaseInfo[phase]; } protected void setPhase(int newPhase) throws IOException { logFile.seek(0L); logFile.writeInt(newPhase); logFile.getFD().sync(); phase = newPhase; } public final synchronized void begin() throws IOException { while (phase != FREE) { try { wait(); } catch (InterruptedException exc) { } } // Change the transaction state. setPhase(RUN); } public String[] getList(String prefix) { return dir.list(new StartWithFilter(prefix)); } public final void create(Serializable obj, String name) throws IOException { save(obj, null, name, true); } public final void create(Serializable obj, String dirName, String name) throws IOException { save(obj, dirName, name, true); } public final void save(Serializable obj, String name) throws IOException { save(obj, null, name, false); } public final void save(Serializable obj, String dirName, String name) throws IOException { save(obj, dirName, name, false); } public void save(Serializable obj, String dirName, String name, boolean first) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(obj); oos.flush(); saveByteArray(bos.toByteArray(), dirName, name); } public final void createByteArray(byte[] buf, String name) throws IOException { saveByteArray(buf, null, name, true, true); } public final void createByteArray(byte[] buf, String dirName, String name) throws IOException { saveByteArray(buf, dirName, name, true, true); } public final void saveByteArray(byte[] buf, String name) throws IOException { saveByteArray(buf, null, name, true, false); } public final void saveByteArray(byte[] buf, String dirName, String name) throws IOException { saveByteArray(buf, dirName, name, true, false); } public void saveByteArray(byte[] buf, String dirName, String name, boolean copy, boolean first) throws IOException { if (phase == RUN) { // We are during a transaction put the new state in the log. Object key = OperationKey.newKey(dirName, name); log.put(key, Operation.alloc(Operation.SAVE, dirName, name, buf)); } else { // Save the new state on the disk. File file; if (dirName == null) { file = new File(dir, name); } else { File parentDir = new File(dir, dirName); if (!parentDir.exists()) { parentDir.mkdirs(); } file = new File(parentDir, name); } try (FileOutputStream fos = new FileOutputStream(file)) { fos.write(buf); } } } public final Object load(String name) throws IOException, ClassNotFoundException { return load(null, name); } public final Object load(String dirName, String name) throws IOException, ClassNotFoundException { byte[] buf = loadByteArray(dirName, name); if (buf != null) { ByteArrayInputStream bis = new ByteArrayInputStream(buf); ObjectInputStream ois = new ObjectInputStream(bis); try { return ois.readObject(); } finally { ois.close(); bis.close(); } } return null; } /** * Returns false, this Transaction implementation does not implement an optimized loadAll method. * * @return false. */ @Override public boolean useLoadAll() { return false; } /** * Fills the map with all objects of the component whose name begins with the prefix. * * @param prefix The prefix of searched objects. * @param map The map of corresponding objects. */ @Override public void loadAll(String prefix, Map map) { if (logmon.isLoggable(BasicLevel.DEBUG)) logmon.log(BasicLevel.DEBUG, "JTransaction, loadAll(" + prefix + ")"); // TODO (AF): To be implemented return; } public final byte[] loadByteArray(String name) throws IOException { return loadByteArray(null, name); } public byte[] loadByteArray(String dirName, String name) throws IOException { if (phase == RUN) { // first search in the log a new value for the object. Object key = OperationKey.newKey(dirName, name); Operation op = (Operation) log.get(key); if (op != null) { if (op.type == Operation.SAVE) { return op.value; } else if (op.type == Operation.DELETE) { // The object is no longer alive return null; } } } try { File file; if (dirName == null) { file = new File(dir, name); } else { File parentDir = new File(dir, dirName); file = new File(parentDir, name); } byte[] buf = null; try (FileInputStream fis = new FileInputStream(file)) { buf = new byte[(int) file.length()]; for (int nb=0; nb this.dir.getAbsolutePath().length()) { deleteDir(dir.getParentFile()); } } } public synchronized void commit(boolean release) throws IOException { if (phase != RUN) throw new NotActiveException("Can not commit inexistent transaction."); // Save the log to disk logFile.seek(4L); for (Enumeration e = log.elements(); e.hasMoreElements(); ) { Operation op = (Operation) e.nextElement(); logFile.writeUTF(op.name); if (op.dirName != null) { logFile.writeUTF(op.dirName); } else { logFile.writeUTF(EMPTY_STRING); } logFile.writeByte(op.type); if (op.type == Operation.SAVE) { logFile.writeInt(op.value.length); logFile.write(op.value); } } logFile.writeUTF(""); setPhase(COMMIT); _commit(); commitCount += 1; log.clear(); if (release) { // Change the transaction state and save it. setPhase(FREE); notify(); } } private void _commit() throws IOException { for (Enumeration e = log.elements(); e.hasMoreElements(); ) { Operation op = (Operation) e.nextElement(); if (op.type == Operation.SAVE) { File file; if (op.dirName == null) { file = new File(dir, op.name); } else { File parentDir = new File(dir, op.dirName); if (!parentDir.exists()) { parentDir.mkdirs(); } file = new File(parentDir, op.name); } try (FileOutputStream fos = new FileOutputStream(file)) { fos.write(op.value); fos.getFD().sync(); } } else if (op.type == Operation.DELETE) { File file; if (op.dirName == null) { file = new File(dir, op.name); file.delete(); } else { File parentDir = new File(dir, op.dirName); file = new File(parentDir, op.name); file.delete(); deleteDir(parentDir); } } else { throw new InvalidObjectException("Unknow object in log."); } } } public synchronized void release() throws IOException { if ((phase != RUN) && (phase != COMMIT) && (phase != ROLLBACK)) throw new IllegalStateException("Can not release transaction."); // Change the transaction state. setPhase(FREE); // wake-up an eventually user's thread in begin notify(); } /** * Stops the transaction module. * It waits all transactions termination, then the module is kept * in a FREE 'ready to use' state. */ public synchronized void stop() { while (phase != FREE) { try { // Wait for the transaction subsystem to be free wait(); } catch (InterruptedException exc) { } } } /** * Close the transaction module. * It waits all transactions termination, the module will be initialized * anew before reusing it. */ public synchronized void close() { stop(); try { setPhase(FINALIZE); logFile.close(); setPhase(INIT); } catch (IOException exc) { } } /** * Indicates whether some operations have been done in * this transaction. */ public boolean containsOperations() { return log.size() > 0; } /** * Returns the number of operations to be committed. * @return the number of operations to be committed. */ public int getOperationCount() { return log.size(); } /** * Backups the content of Transaction module. * Produces a generic Backup/Restore file containing all living objects. * Currently not implemented, throws an UnsupportedOperationException. * * @param path Directory path to store the backup. * @return the name of created backup file. * @throws Exception An error occurs during backup. */ @Override public String backup(String path) throws Exception { // /!\ Be careful, should be synchronized!! throw new UnsupportedOperationException("Backup operation not already implemented."); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy