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

org.ehcache.transactions.xa.internal.journal.PersistentJournal Maven / Gradle / Ivy

/*
 * Copyright Terracotta, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.ehcache.transactions.xa.internal.journal;

import org.ehcache.spi.serialization.Serializer;
import org.ehcache.transactions.xa.internal.TransactionId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import static org.ehcache.core.internal.util.TypeUtil.uncheckedCast;

/**
 * A persistent, but not durable {@link Journal} implementation.
 * This implementation will persist saved states during close and restore them during open. If close is not called,
 * all saved states are lost.
 *
 * @author Ludovic Orban
 */
public class PersistentJournal extends TransientJournal {

  private static final Logger LOGGER = LoggerFactory.getLogger(PersistentJournal.class);
  private static final String JOURNAL_FILENAME = "journal.data";

  protected static class SerializableEntry implements Serializable {
    private static final long serialVersionUID = -6586025792671381923L;
    final XAState state;
    final boolean heuristic;
    final Collection serializedKeys;
    protected SerializableEntry(Entry entry, Serializer keySerializer) {
      this.state = entry.state;
      this.heuristic = entry.heuristic;
      this.serializedKeys = new ArrayList<>();
      for (K key : entry.keys) {
        ByteBuffer byteBuffer = keySerializer.serialize(key);
        byte[] bytes = new byte[byteBuffer.remaining()];
        byteBuffer.get(bytes);
        this.serializedKeys.add(bytes);
      }
    }

    protected Collection deserializeKeys(Serializer keySerializer) throws ClassNotFoundException {
      Collection result = new ArrayList<>();
      for (byte[] serializedKey : serializedKeys) {
        K key = keySerializer.read(ByteBuffer.wrap(serializedKey));
        result.add(key);
      }
      return result;
    }
  }


  private final File directory;
  private final Serializer keySerializer;

  public PersistentJournal(File directory, Serializer keySerializer) {
    if (directory == null) {
      throw new NullPointerException("directory must not be null");
    }
    if (keySerializer == null) {
      throw new NullPointerException("keySerializer must not be null");
    }
    this.directory = directory;
    this.keySerializer = keySerializer;
  }

  @Override
  public void open() throws IOException {
    File file = new File(directory, JOURNAL_FILENAME);
    if (file.isFile()) {
      try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) {
        boolean valid = ois.readBoolean();
        states.clear();
        if (valid) {
          Map> readStates = uncheckedCast(ois.readObject());
          for (Map.Entry> entry : readStates.entrySet()) {
            SerializableEntry value = entry.getValue();
            states.put(entry.getKey(), new Entry<>(value.state, value.heuristic, value.deserializeKeys(keySerializer)));
          }
        }
      } catch (IOException ioe) {
        LOGGER.warn("Cannot read XA journal, truncating it", ioe);
      } catch (ClassNotFoundException cnfe) {
        LOGGER.warn("Cannot deserialize XA journal contents, truncating it", cnfe);
      } finally {
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file))) {
          oos.writeObject(false);
        }
      }
    }
  }

  @Override
  public void close() throws IOException {
    try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File(directory, JOURNAL_FILENAME)))) {
      oos.writeBoolean(true);
      Map> toSerialize = new HashMap<>();
      for (Map.Entry> entry : states.entrySet()) {
        TransactionId key = entry.getKey();
        Entry value = entry.getValue();
        toSerialize.put(key, new SerializableEntry<>(value, keySerializer));
      }
      oos.writeObject(toSerialize);
      states.clear();
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy