/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.apache.jackrabbit.core.journal; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.channels.FileLock; /** * Maintains a file-based revision counter with locking, assuring uniqueness. */ class LockableFileRevision { /** * Logger. */ private static final Logger log = LoggerFactory.getLogger(LockableFileRevision.class); /** * Underlying file. */ private final File file; /** * Underlying random access file. */ private RandomAccessFile raf; /** * File lock. */ private FileLock lock; /** * Current lock count. */ private int locks; /** * Creates a new file based revision counter. * * @param file holding global counter */ public LockableFileRevision(File file) { this.file = file; try { if (!file.exists()) { file.createNewFile(); } } catch (IOException e) { String msg = "I/O error while attempting to create new file '" + file + "': " + e.getMessage(); log.warn(msg); } } /** * Lock underlying file. * * @param shared whether to allow other readers or not */ public synchronized void lock(boolean shared) throws JournalException { if (lock == null) { try { raf = new RandomAccessFile(file, shared ? "r" : "rw"); lock = raf.getChannel().lock(0L, Long.MAX_VALUE, shared); } catch (IOException e) { String msg = "I/O error occurred."; throw new JournalException(msg, e); } finally { if (lock == null && raf != null) { try { raf.close(); } catch (IOException e) { String msg = "I/O error while closing file " + file.getPath() + ": " + e.getMessage(); log.warn(msg); } raf = null; } } } locks++; } /** * Unlock underlying file. */ public synchronized void unlock() { if (lock != null && --locks == 0) { try { lock.release(); } catch (IOException e) { String msg = "I/O error while releasing lock: " + e.getMessage(); log.warn(msg); } lock = null; if (raf != null) { try { raf.close(); } catch (IOException e) { String msg = "I/O error while closing file: " + e.getMessage(); log.warn(msg); } } raf = null; } } /** * Return current counter value. * * @return counter value * @throws JournalException if some error occurs */ public long get() throws JournalException { lock(true); try { long value = 0L; if (raf.length() > 0) { raf.seek(0L); value = raf.readLong(); } return value; } catch (IOException e) { throw new JournalException("I/O error occurred: ", e); } finally { unlock(); } } /** * Set current counter value. * * @param value new counter value * @throws JournalException if some error occurs */ public void set(long value) throws JournalException { lock(false); try { raf.seek(0L); raf.writeLong(value); } catch (IOException e) { throw new JournalException("I/O error occurred.", e); } finally { unlock(); } } }