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     * Developers:    Sasa Markovic (saxon@jrobin.org)
011     *
012     *
013     * This library is free software; you can redistribute it and/or modify it under the terms
014     * of the GNU Lesser General Public License as published by the Free Software Foundation;
015     * either version 2.1 of the License, or (at your option) any later version.
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.util.HashMap;
030    
031    /**
032     * Base (abstract) backend factory class which holds references to all concrete
033     * backend factories and defines abstract methods which must be implemented in
034     * all concrete factory implementations.<p>
035     * <p/>
036     * Factory classes are used to create concrete {@link RrdBackend} implementations.
037     * Each factory creates unlimited number of specific backend objects.
038     * <p/>
039     * JRobin supports four different backend types (backend factories) out of the box:<p>
040     * <ul>
041     * <li>{@link RrdFileBackend}: objects of this class are created from the
042     * {@link RrdFileBackendFactory} class. This was the default backend used in all
043     * JRobin releases before 1.4.0 release. It uses java.io.* package and RandomAccessFile class to store
044     * RRD data in files on the disk.
045     * <p/>
046     * <li>{@link RrdSafeFileBackend}: objects of this class are created from the
047     * {@link RrdSafeFileBackendFactory} class. It uses java.io.* package and RandomAccessFile class to store
048     * RRD data in files on the disk. This backend is SAFE:
049     * it locks the underlying RRD file during update/fetch operations, and caches only static
050     * parts of a RRD file in memory. Therefore, this backend is safe to be used when RRD files should
051     * be shared <b>between several JVMs</b> at the same time. However, this backend is *slow* since it does
052     * not use fast java.nio.* package (it's still based on the RandomAccessFile class).
053     * <p/>
054     * <li>{@link RrdNioBackend}: objects of this class are created from the
055     * {@link RrdNioBackendFactory} class. The backend uses java.io.* and java.nio.*
056     * classes (mapped ByteBuffer) to store RRD data in files on the disk. This is the default backend
057     * since 1.4.0 release.
058     * <p/>
059     * <li>{@link RrdMemoryBackend}: objects of this class are created from the
060     * {@link RrdMemoryBackendFactory} class. This backend stores all data in memory. Once
061     * JVM exits, all data gets lost. The backend is extremely fast and memory hungry.
062     * </ul>
063     * <p/>
064     * Each backend factory is identifed by its {@link #getFactoryName() name}. Constructors
065     * are provided in the {@link RrdDb} class to create RrdDb objects (RRD databases)
066     * backed with a specific backend.<p>
067     * <p/>
068     * See javadoc for {@link RrdBackend} to find out how to create your custom backends.
069     */
070    public abstract class RrdBackendFactory {
071            private static final HashMap<String, RrdBackendFactory> factories = new HashMap<String, RrdBackendFactory>();
072            private static RrdBackendFactory defaultFactory;
073    
074            static {
075                    try {
076                            RrdFileBackendFactory fileFactory = new RrdFileBackendFactory();
077                            registerFactory(fileFactory);
078                            RrdMemoryBackendFactory memoryFactory = new RrdMemoryBackendFactory();
079                            registerFactory(memoryFactory);
080                            RrdNioBackendFactory nioFactory = new RrdNioBackendFactory();
081                            registerFactory(nioFactory);
082                            RrdSafeFileBackendFactory safeFactory = new RrdSafeFileBackendFactory();
083                            registerFactory(safeFactory);
084                            selectDefaultFactory();
085                    }
086                    catch (RrdException e) {
087                            throw new RuntimeException("FATAL: Cannot register RRD backend factories: " + e);
088                    }
089            }
090    
091            private static void selectDefaultFactory() throws RrdException {
092                    String version = System.getProperty("java.version");
093                    if (version == null || version.startsWith("1.3.") ||
094                                    version.startsWith("1.4.0") || version.startsWith("1.4.1")) {
095                            setDefaultFactory("FILE");
096                    }
097                    else {
098                            setDefaultFactory("NIO");
099                    }
100            }
101    
102            /**
103             * Returns backend factory for the given backend factory name.
104             *
105             * @param name Backend factory name. Initially supported names are:<p>
106             *             <ul>
107             *             <li><b>FILE</b>: Default factory which creates backends based on the
108             *             java.io.* package. RRD data is stored in files on the disk
109             *             <li><b>SAFE</b>: Default factory which creates backends based on the
110             *             java.io.* package. RRD data is stored in files on the disk. This backend
111             *             is "safe". Being safe means that RRD files can be safely shared between
112             *             several JVM's.
113             *             <li><b>NIO</b>: Factory which creates backends based on the
114             *             java.nio.* package. RRD data is stored in files on the disk
115             *             <li><b>MEMORY</b>: Factory which creates memory-oriented backends.
116             *             RRD data is stored in memory, it gets lost as soon as JVM exits.
117             *             </ul>
118             * @return Backend factory for the given factory name
119             * @throws RrdException Thrown if no factory with the given name
120             *                      is available.
121             */
122            public static synchronized RrdBackendFactory getFactory(String name) throws RrdException {
123                    RrdBackendFactory factory = factories.get(name);
124                    if (factory != null) {
125                            return factory;
126                    }
127                    else {
128                            throw new RrdException("No backend factory found with the name specified [" + name + "]");
129                    }
130            }
131    
132            /**
133             * Registers new (custom) backend factory within the JRobin framework.
134             *
135             * @param factory Factory to be registered
136             * @throws RrdException Thrown if the name of the specified factory is already
137             *                      used.
138             */
139            public static synchronized void registerFactory(RrdBackendFactory factory)
140                            throws RrdException {
141                    String name = factory.getFactoryName();
142                    if (!factories.containsKey(name)) {
143                            factories.put(name, factory);
144                    }
145                    else {
146                            throw new RrdException("Backend factory of this name2 (" + name +
147                                            ") already exists and cannot be registered");
148                    }
149            }
150    
151            /**
152             * Registers new (custom) backend factory within the JRobin framework and sets this
153             * factory as the default.
154             *
155             * @param factory Factory to be registered and set as default
156             * @throws RrdException Thrown if the name of the specified factory is already
157             *                      used.
158             */
159            public static synchronized void registerAndSetAsDefaultFactory(RrdBackendFactory factory)
160                            throws RrdException {
161                    registerFactory(factory);
162                    setDefaultFactory(factory.getFactoryName());
163            }
164    
165            /**
166             * Returns the defaul backend factory. This factory is used to construct
167             * {@link RrdDb} objects if no factory is specified in the RrdDb constructor.
168             *
169             * @return Default backend factory.
170             */
171            public static RrdBackendFactory getDefaultFactory() {
172                    return defaultFactory;
173            }
174    
175            /**
176             * Replaces the default backend factory with a new one. This method must be called before
177             * the first RRD gets created. <p>
178             *
179             * @param factoryName Name of the default factory. Out of the box, JRobin supports four
180             *                    different RRD backends: "FILE" (java.io.* based), "SAFE" (java.io.* based - use this
181             *                    backend if RRD files may be accessed from several JVMs at the same time),
182             *                    "NIO" (java.nio.* based) and "MEMORY" (byte[] based).
183             * @throws RrdException Thrown if invalid factory name is supplied or not called before
184             *                      the first RRD is created.
185             */
186            public static void setDefaultFactory(String factoryName) throws RrdException {
187                    // We will allow this only if no RRDs are created
188                    if (!RrdBackend.isInstanceCreated()) {
189                            defaultFactory = getFactory(factoryName);
190                    }
191                    else {
192                            throw new RrdException("Could not change the default backend factory. " +
193                                            "This method must be called before the first RRD gets created");
194                    }
195            }
196    
197            /**
198             * Creates RrdBackend object for the given storage path.
199             *
200             * @param path   Storage path
201             * @param readOnly True, if the storage should be accessed in read/only mode.
202             *                 False otherwise.
203             * @return Backend object which handles all I/O operations for the given storage path
204             * @throws IOException Thrown in case of I/O error.
205             */
206            protected abstract RrdBackend open(String path, boolean readOnly) throws IOException;
207    
208            /**
209             * Method to determine if a storage with the given path already exists.
210             *
211             * @param path Storage path
212             * @return True, if such storage exists, false otherwise.
213             */
214            protected abstract boolean exists(String path) throws IOException;
215    
216            /**
217             * Returns the name (primary ID) for the factory.
218             *
219             * @return Name of the factory.
220             */
221            public abstract String getFactoryName();
222    }