/* * Copyright (C) 2008 Jive Software. All rights reserved. * * Licensed 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.jivesoftware.openfire.reporting.stats; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import org.jivesoftware.database.DbConnectionManager; import org.jrobin.core.RrdBackend; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class RrdSqlBackend extends RrdBackend { private static final Logger Log = LoggerFactory.getLogger(RrdSqlBackend.class); // SQL prepared statements static final String JDBC_SELECT = "SELECT bytes from ofRRDs where id = ?"; static final String JDBC_INSERT = "INSERT INTO ofRRDs (id, updatedDate, bytes) VALUES (?, ?, ?)"; static final String JDBC_UPDATE = "UPDATE ofRRDs SET bytes = ?, updatedDate=? WHERE id = ?"; static final String JDBC_DELETE = "DELETE FROM ofRRDs WHERE id = ?"; // this is the place where our RRD bytes will be stored private byte[] buffer = null; // When readOnly then the SQL DB is not updated private boolean readOnly; public static void importRRD(String id, File rrdFile) throws IOException { // Read content from file FileInputStream stream = null; byte[] bytes = null; try { stream = new FileInputStream(rrdFile); // Create the byte array to hold the data bytes = new byte[(int) rrdFile.length()]; // Read in the bytes int offset = 0; int numRead; while (offset < bytes.length && (numRead = stream.read(bytes, offset, bytes.length - offset)) >= 0) { offset += numRead; } } finally { if (stream != null) { stream.close(); } } // Save file content to the DB Connection con = null; PreparedStatement pstmt = null; PreparedStatement insertStmt = null; ResultSet rs = null; try { con = DbConnectionManager.getConnection(); pstmt = con.prepareStatement(JDBC_SELECT); pstmt.setString(1, id); rs = pstmt.executeQuery(); if(rs.next()) { // Do not import since there is already an RRD in the DB } else { // RRD with the given id does not exist // we'll insert a new row in the table using the supplied id // but with no RRD bytes (null) insertStmt = con.prepareStatement(JDBC_INSERT); insertStmt.setString(1, id); insertStmt.setLong(2, System.currentTimeMillis()); insertStmt.setBytes(3, bytes); insertStmt.executeUpdate(); } } catch (Exception e) { Log.error("Error while accessing information in database: " + e); } finally { DbConnectionManager.closeStatement(insertStmt); DbConnectionManager.closeConnection(rs, pstmt, con); } } RrdSqlBackend(String id, boolean readOnly) throws IOException { super(id); this.readOnly = readOnly; Connection con = null; PreparedStatement pstmt = null; PreparedStatement insertStmt = null; ResultSet rs = null; try { con = DbConnectionManager.getConnection(); pstmt = con.prepareStatement(JDBC_SELECT); pstmt.setString(1, id); rs = pstmt.executeQuery(); if(rs.next()) { // RRD with the given id already exists // bring RRD data to our buffer buffer = rs.getBytes("bytes"); } else { // RRD with the given id does not exist // we'll insert a new row in the table using the supplied id // but with no RRD bytes (null) insertStmt = con.prepareStatement(JDBC_INSERT); insertStmt.setString(1, id); insertStmt.setLong(2, System.currentTimeMillis()); insertStmt.setBytes(3, null); insertStmt.executeUpdate(); } } catch (Exception e) { Log.error("Error while accessing information in database: " + e); } finally { DbConnectionManager.closeStatement(insertStmt); DbConnectionManager.closeConnection(rs, pstmt, con); } } // this method writes bytes supplied from the JRobin frontend // to our memory buffer @Override protected void write(long offset, byte[] b) { int pos = (int) offset; for(int i = 0; i < b.length; i++) { buffer[pos++] = b[i]; } } // this method reads bytes requested from the JRobin frontend // and stores them in the supplied byte[] array @Override protected void read(long offset, byte[] b) { int pos = (int) offset; for(int i = 0; i < b.length; i++) { b[i] = buffer[pos++]; } } // returns the RRD size (since all RRD bytes are // in the buffer, it is equal to the buffer length @Override public long getLength() throws IOException { return buffer.length; } // provides enough space in memory for the RRD @Override protected void setLength(long length) { buffer = new byte[(int) length]; } @Override public void close() throws IOException { super.close(); // Save data to the SQL DB only if not read-only if (!readOnly) { sync(); } } // sends bytes in memory to the database protected void sync() throws IOException { // RRD id is here String id = super.getPath(); Connection con = null; PreparedStatement pstmt = null; ResultSet rs = null; try { con = DbConnectionManager.getConnection(); pstmt = con.prepareStatement(JDBC_UPDATE); pstmt.setBytes(1, buffer); pstmt.setLong(2, System.currentTimeMillis()); pstmt.setString(3, id); pstmt.executeUpdate(); } catch (Exception e) { Log.error("Error while updating information in database: " + e); } finally { DbConnectionManager.closeConnection(rs, pstmt, con); } } // checks if RRD with the given id already exists in the database // used from RrdSqlBackendFactory class static boolean exists(String id) throws IOException { Connection con = null; PreparedStatement pstmt = null; ResultSet rs = null; try { con = DbConnectionManager.getConnection(); pstmt = con.prepareStatement(JDBC_SELECT); pstmt.setString(1, id); rs = pstmt.executeQuery(); return rs.next(); } catch (Exception e) { Log.error("Error while accessing information in database: " + e); } finally { DbConnectionManager.closeConnection(rs, pstmt, con); } return false; } }