1 /* ============================================================
2 * JRobin : Pure java implementation of RRDTool's functionality
3 * ============================================================
4 *
5 * Project Info: http://www.jrobin.org
6 * Project Lead: Sasa Markovic (saxon@jrobin.org);
7 *
8 * (C) Copyright 2003-2005, by Sasa Markovic.
9 *
10 * Developers: Sasa Markovic (saxon@jrobin.org)
11 *
12 *
13 * This library is free software; you can redistribute it and/or modify it under the terms
14 * of the GNU Lesser General Public License as published by the Free Software Foundation;
15 * either version 2.1 of the License, or (at your option) any later version.
16 *
17 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
18 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19 * See the GNU Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public License along with this
22 * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
23 * Boston, MA 02111-1307, USA.
24 */
25
26 package org.jrobin.core;
27
28 import java.io.IOException;
29
30 /**
31 * Base implementation class for all backend classes. Each Round Robin Database object
32 * ({@link RrdDb} object) is backed with a single RrdBackend object which performs
33 * actual I/O operations on the underlying storage. JRobin supports
34 * three different bakcends out of the box:</p>
35 * <ul>
36 * <li>{@link RrdFileBackend}: objects of this class are created from the
37 * {@link RrdFileBackendFactory} class. This was the default backend used in all
38 * JRobin releases prior to 1.4.0. It uses java.io.* package and
39 * RandomAccessFile class to store RRD data in files on the disk.
40 * <p/>
41 * <li>{@link RrdNioBackend}: objects of this class are created from the
42 * {@link RrdNioBackendFactory} class. The backend uses java.io.* and java.nio.*
43 * classes (mapped ByteBuffer) to store RRD data in files on the disk. This backend is fast, very fast,
44 * but consumes a lot of memory (borrowed not from the JVM but from the underlying operating system
45 * directly). <b>This is the default backend used in JRobin since 1.4.0 release.</b>
46 * <p/>
47 * <li>{@link RrdMemoryBackend}: objects of this class are created from the
48 * {@link RrdMemoryBackendFactory} class. This backend stores all data in memory. Once
49 * JVM exits, all data gets lost. The backend is extremely fast and memory hungry.
50 * </ul>
51 * <p/>
52 * To create your own backend in order to provide some custom type of RRD storage,
53 * you should do the following:</p>
54 * <p/>
55 * <ul>
56 * <li>Create your custom RrdBackend class (RrdCustomBackend, for example)
57 * by extending RrdBackend class. You have to implement all abstract methods defined
58 * in the base class.
59 * <p/>
60 * <li>Create your custom RrdBackendFactory class (RrdCustomBackendFactory,
61 * for example) by extending RrdBackendFactory class. You have to implement all
62 * abstract methods defined in the base class. Your custom factory class will actually
63 * create custom backend objects when necessary.
64 * <p/>
65 * <li>Create instance of your custom RrdBackendFactory and register it as a regular
66 * factory available to JRobin framework. See javadoc for {@link RrdBackendFactory} to
67 * find out how to do this
68 * </ul>
69 */
70 public abstract class RrdBackend {
71 private static boolean instanceCreated = false;
72 private String path;
73
74 /**
75 * Creates backend for a RRD storage with the given path.
76 *
77 * @param path String identifying RRD storage. For files on the disk, this
78 * argument should represent file path. Other storage types might interpret
79 * this argument differently.
80 */
81 protected RrdBackend(String path) {
82 this.path = path;
83 instanceCreated = true;
84 }
85
86 /**
87 * Returns path to the storage.
88 *
89 * @return Storage path
90 */
91 public String getPath() {
92 return path;
93 }
94
95 /**
96 * Writes an array of bytes to the underlying storage starting from the given
97 * storage offset.
98 *
99 * @param offset Storage offset.
100 * @param b Array of bytes that should be copied to the underlying storage
101 * @throws IOException Thrown in case of I/O error
102 */
103 protected abstract void write(long offset, byte[] b) throws IOException;
104
105 /**
106 * Reads an array of bytes from the underlying storage starting from the given
107 * storage offset.
108 *
109 * @param offset Storage offset.
110 * @param b Array which receives bytes from the underlying storage
111 * @throws IOException Thrown in case of I/O error
112 */
113 protected abstract void read(long offset, byte[] b) throws IOException;
114
115 /**
116 * Returns the number of RRD bytes in the underlying storage.
117 *
118 * @return Number of RRD bytes in the storage.
119 * @throws IOException Thrown in case of I/O error.
120 */
121 public abstract long getLength() throws IOException;
122
123 /**
124 * Sets the number of bytes in the underlying RRD storage.
125 * This method is called only once, immediately after a new RRD storage gets created.
126 *
127 * @param length Length of the underlying RRD storage in bytes.
128 * @throws IOException Thrown in case of I/O error.
129 */
130 protected abstract void setLength(long length) throws IOException;
131
132 /**
133 * Closes the underlying backend.
134 *
135 * @throws IOException Thrown in case of I/O error
136 */
137 public void close() throws IOException {
138 }
139
140 /**
141 * This method suggests the caching policy to the JRobin frontend (high-level) classes. If <code>true</code>
142 * is returned, frontent classes will cache frequently used parts of a RRD file in memory to improve
143 * performance. If </code>false</code> is returned, high level classes will never cache RRD file sections
144 * in memory.
145 *
146 * @return <code>true</code> if file caching is enabled, <code>false</code> otherwise. By default, the
147 * method returns <code>true</code> but it can be overriden in subclasses.
148 */
149 protected boolean isCachingAllowed() {
150 return true;
151 }
152
153 /**
154 * Reads all RRD bytes from the underlying storage
155 *
156 * @return RRD bytes
157 * @throws IOException Thrown in case of I/O error
158 */
159 public final byte[] readAll() throws IOException {
160 byte[] b = new byte[(int) getLength()];
161 read(0, b);
162 return b;
163 }
164
165 final void writeInt(long offset, int value) throws IOException {
166 write(offset, getIntBytes(value));
167 }
168
169 final void writeLong(long offset, long value) throws IOException {
170 write(offset, getLongBytes(value));
171 }
172
173 final void writeDouble(long offset, double value) throws IOException {
174 write(offset, getDoubleBytes(value));
175 }
176
177 final void writeDouble(long offset, double value, int count) throws IOException {
178 byte[] b = getDoubleBytes(value);
179 byte[] image = new byte[8 * count];
180 for (int i = 0, k = 0; i < count; i++) {
181 image[k++] = b[0];
182 image[k++] = b[1];
183 image[k++] = b[2];
184 image[k++] = b[3];
185 image[k++] = b[4];
186 image[k++] = b[5];
187 image[k++] = b[6];
188 image[k++] = b[7];
189 }
190 write(offset, image);
191 }
192
193 final void writeDouble(long offset, double[] values) throws IOException {
194 int count = values.length;
195 byte[] image = new byte[8 * count];
196 for (int i = 0, k = 0; i < count; i++) {
197 byte[] b = getDoubleBytes(values[i]);
198 image[k++] = b[0];
199 image[k++] = b[1];
200 image[k++] = b[2];
201 image[k++] = b[3];
202 image[k++] = b[4];
203 image[k++] = b[5];
204 image[k++] = b[6];
205 image[k++] = b[7];
206 }
207 write(offset, image);
208 }
209
210 final void writeString(long offset, String value) throws IOException {
211 value = value.trim();
212 byte[] b = new byte[RrdPrimitive.STRING_LENGTH * 2];
213 for (int i = 0, k = 0; i < RrdPrimitive.STRING_LENGTH; i++) {
214 char c = (i < value.length()) ? value.charAt(i) : ' ';
215 byte[] cb = getCharBytes(c);
216 b[k++] = cb[0];
217 b[k++] = cb[1];
218 }
219 write(offset, b);
220 }
221
222 final int readInt(long offset) throws IOException {
223 byte[] b = new byte[4];
224 read(offset, b);
225 return getInt(b);
226 }
227
228 final long readLong(long offset) throws IOException {
229 byte[] b = new byte[8];
230 read(offset, b);
231 return getLong(b);
232 }
233
234 final double readDouble(long offset) throws IOException {
235 byte[] b = new byte[8];
236 read(offset, b);
237 return getDouble(b);
238 }
239
240 final double[] readDouble(long offset, int count) throws IOException {
241 int byteCount = 8 * count;
242 byte[] image = new byte[byteCount];
243 read(offset, image);
244 double[] values = new double[count];
245 for (int i = 0, k = -1; i < count; i++) {
246 byte[] b = new byte[] {
247 image[++k], image[++k], image[++k], image[++k],
248 image[++k], image[++k], image[++k], image[++k]
249 };
250 values[i] = getDouble(b);
251 }
252 return values;
253 }
254
255 final String readString(long offset) throws IOException {
256 byte[] b = new byte[RrdPrimitive.STRING_LENGTH * 2];
257 char[] c = new char[RrdPrimitive.STRING_LENGTH];
258 read(offset, b);
259 for (int i = 0, k = -1; i < RrdPrimitive.STRING_LENGTH; i++) {
260 byte[] cb = new byte[] {b[++k], b[++k]};
261 c[i] = getChar(cb);
262 }
263 return new String(c).trim();
264 }
265
266 // static helper methods
267
268 private static byte[] getIntBytes(int value) {
269 byte[] b = new byte[4];
270 b[0] = (byte) ((value >>> 24) & 0xFF);
271 b[1] = (byte) ((value >>> 16) & 0xFF);
272 b[2] = (byte) ((value >>> 8) & 0xFF);
273 b[3] = (byte) ((value) & 0xFF);
274 return b;
275 }
276
277 private static byte[] getLongBytes(long value) {
278 byte[] b = new byte[8];
279 b[0] = (byte) ((int) (value >>> 56) & 0xFF);
280 b[1] = (byte) ((int) (value >>> 48) & 0xFF);
281 b[2] = (byte) ((int) (value >>> 40) & 0xFF);
282 b[3] = (byte) ((int) (value >>> 32) & 0xFF);
283 b[4] = (byte) ((int) (value >>> 24) & 0xFF);
284 b[5] = (byte) ((int) (value >>> 16) & 0xFF);
285 b[6] = (byte) ((int) (value >>> 8) & 0xFF);
286 b[7] = (byte) ((int) (value) & 0xFF);
287 return b;
288 }
289
290 private static byte[] getCharBytes(char value) {
291 byte[] b = new byte[2];
292 b[0] = (byte) ((value >>> 8) & 0xFF);
293 b[1] = (byte) ((value) & 0xFF);
294 return b;
295 }
296
297 private static byte[] getDoubleBytes(double value) {
298 return getLongBytes(Double.doubleToLongBits(value));
299 }
300
301 private static int getInt(byte[] b) {
302 assert b.length == 4: "Invalid number of bytes for integer conversion";
303 return ((b[0] << 24) & 0xFF000000) + ((b[1] << 16) & 0x00FF0000) +
304 ((b[2] << 8) & 0x0000FF00) + (b[3] & 0x000000FF);
305 }
306
307 private static long getLong(byte[] b) {
308 assert b.length == 8: "Invalid number of bytes for long conversion";
309 int high = getInt(new byte[] {b[0], b[1], b[2], b[3]});
310 int low = getInt(new byte[] {b[4], b[5], b[6], b[7]});
311 return ((long) (high) << 32) + (low & 0xFFFFFFFFL);
312 }
313
314 private static char getChar(byte[] b) {
315 assert b.length == 2: "Invalid number of bytes for char conversion";
316 return (char) (((b[0] << 8) & 0x0000FF00)
317 + (b[1] & 0x000000FF));
318 }
319
320 private static double getDouble(byte[] b) {
321 assert b.length == 8: "Invalid number of bytes for double conversion";
322 return Double.longBitsToDouble(getLong(b));
323 }
324
325 static boolean isInstanceCreated() {
326 return instanceCreated;
327 }
328 }
329