View Javadoc

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 }