001 /* ============================================================
002 * JRobin : Pure java implementation of RRDTool's functionality
003 * ============================================================
004 *
005 * Project Info: http://www.jrobin.org
006 * Project Lead: Sasa Markovic (saxon@jrobin.org);
007 *
008 * (C) Copyright 2003-2005, by Sasa Markovic.
009 *
010 * This library is free software; you can redistribute it and/or modify it under the terms
011 * of the GNU Lesser General Public License as published by the Free Software Foundation;
012 * either version 2.1 of the License, or (at your option) any later version.
013 *
014 * Developers: Sasa Markovic (saxon@jrobin.org)
015 *
016 *
017 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
018 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
019 * See the GNU Lesser General Public License for more details.
020 *
021 * You should have received a copy of the GNU Lesser General Public License along with this
022 * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
023 * Boston, MA 02111-1307, USA.
024 */
025
026 package org.jrobin.core;
027
028 import java.io.IOException;
029 import java.nio.channels.FileLock;
030 import java.nio.channels.FileChannel;
031
032 /**
033 * JRobin backend which is used to store RRD data to ordinary files on the disk. This backend
034 * is SAFE: it locks the underlying RRD file during update/fetch operations, and caches only static
035 * parts of a RRD file in memory. Therefore, this backend is safe to be used when RRD files should
036 * be shared between several JVMs at the same time. However, this backend is a little bit slow
037 * since it does not use fast java.nio.* package (it's still based on the RandomAccessFile class).
038 */
039 public class RrdSafeFileBackend extends RrdFileBackend {
040 private static final Counters counters = new Counters();
041
042 private FileLock lock;
043
044 /**
045 * Creates RrdFileBackend object for the given file path, backed by RandomAccessFile object.
046 *
047 * @param path Path to a file
048 * @throws IOException Thrown in case of I/O error
049 */
050 public RrdSafeFileBackend(String path, long lockWaitTime, long lockRetryPeriod)
051 throws IOException {
052 super(path, false);
053 try {
054 lockFile(lockWaitTime, lockRetryPeriod);
055 }
056 catch (IOException ioe) {
057 super.close();
058 throw ioe;
059 }
060 }
061
062 private void lockFile(long lockWaitTime, long lockRetryPeriod) throws IOException {
063 long entryTime = System.currentTimeMillis();
064 FileChannel channel = file.getChannel();
065 lock = channel.tryLock(0, Long.MAX_VALUE, false);
066 if (lock != null) {
067 counters.registerQuickLock();
068 return;
069 }
070 do {
071 try {
072 Thread.sleep(lockRetryPeriod);
073 }
074 catch (InterruptedException e) {
075 // NOP
076 }
077 lock = channel.tryLock(0, Long.MAX_VALUE, false);
078 if (lock != null) {
079 counters.registerDelayedLock();
080 return;
081 }
082 } while (System.currentTimeMillis() - entryTime <= lockWaitTime);
083 counters.registerError();
084 throw new IOException("Could not obtain exclusive lock on file: " + getPath() +
085 "] after " + lockWaitTime + " milliseconds");
086 }
087
088 public void close() throws IOException {
089 try {
090 if (lock != null) {
091 lock.release();
092 lock = null;
093 counters.registerUnlock();
094 }
095 }
096 finally {
097 super.close();
098 }
099 }
100
101 /**
102 * Defines the caching policy for this backend.
103 *
104 * @return <code>false</code>
105 */
106 protected boolean isCachingAllowed() {
107 return false;
108 }
109
110 public static String getLockInfo() {
111 return counters.getInfo();
112 }
113
114 static class Counters {
115 long locks, quickLocks, unlocks, locked, errors;
116
117 synchronized void registerQuickLock() {
118 locks++;
119 quickLocks++;
120 locked++;
121 }
122
123 synchronized void registerDelayedLock() {
124 locks++;
125 locked++;
126 }
127
128 synchronized void registerUnlock() {
129 unlocks++;
130 locked--;
131 }
132
133 synchronized void registerError() {
134 errors++;
135 }
136
137 synchronized String getInfo() {
138 return "LOCKS=" + locks + ", " + "UNLOCKS=" + unlocks + ", " +
139 "DELAYED_LOCKS=" + (locks - quickLocks) + ", " + "LOCKED=" + locked + ", " +
140 "ERRORS=" + errors;
141 }
142 }
143 }