package net.sourceforge.javautil.database.encryption;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.lang.management.MemoryType;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.sourceforge.javautil.common.ClassNameUtil;
import net.sourceforge.javautil.common.IOUtil;
import net.sourceforge.javautil.common.ReflectionUtil;
import net.sourceforge.javautil.common.StringUtil;
import net.sourceforge.javautil.common.encryption.IEncryptionProvider;
import net.sourceforge.javautil.common.exception.ThrowableManagerRegistry;
import net.sourceforge.javautil.common.io.IVirtualFile;
import com.Ostermiller.util.Base64;
/**
* This represents a table that is a memory only table encrypted to disk and loaded at runtime.
*
* @author ponder
* @author $Author: ponderator $
* @version $Id: EncryptedMemoryTable.java 1149 2009-10-21 21:34:58Z ponderator $
*/
public class EncryptedTable {
private static Logger log = LoggerFactory.getLogger(EncryptedTable.class);
protected EncryptedTableManager manager;
protected String tableName;
public EncryptedTable(EncryptedTableManager manager, String tableName) {
this.tableName = tableName;
this.manager = manager;
}
/**
* This will load a previously serialized memory table.
*
* @param connection The connection to use for importing
* @param reader The source reader for reading the serialized data
*/
public void load (Connection connection, BufferedReader reader) {
PreparedStatement prepared = null;
Statement statement = null;
try {
String columnLine = reader.readLine();
boolean checkFieldPrefix = true;
if ("VERSION:2".equals(columnLine)) {
checkFieldPrefix = false;
columnLine = reader.readLine();
}
if (columnLine == null) {
log.warn("No data from input stream for " + this.tableName);
return;
}
statement = connection.createStatement();
ResultSet set = statement.executeQuery( "SELECT * FROM " + tableName + " LIMIT 0, 1");
Map<String, String> columns = this.getColumnData(set);
String[] fielddefs = columnLine.split(",");
Map<String, String> fields = new LinkedHashMap<String, String>();
statement.executeUpdate("DELETE FROM " + tableName);
for (String fielddef : fielddefs) {
String[] fd = fielddef.split(":");
if (fd.length == 2) {
fields.put(fd[0], fd[1]);
} else if (fd.length == 1) {
fields.put(fd[0], columns.get(fd[0]));
} else {
log.warn("Could not understand field def: " + fielddef);
}
}
prepared = connection.prepareStatement("INSERT INTO " + tableName + " (" + StringUtil.join(fields.keySet(), ',') + ") VALUES (?" +
StringUtil.repeat(",?", fields.size() - 1) + ")");
String line = null;
List<String> names = new ArrayList<String>(fields.keySet());
log.warn("Loading table: " + tableName + ", fields: " + names);
while ( (line = reader.readLine()) != null ) {
if ("".equals(line)) continue;
String[] cols = line.split(",");
for (int c=1; c<=names.size(); c++) {
String value = cols.length >= c ? Base64.decode(cols[c-1]) : null;
if (":::NULLIFIED:::".equals(value)) value = null;
if (checkFieldPrefix && value != null) {
int idx = value.indexOf(':');
if (idx != -1) {
value = idx+1 == value.length() ? null : value.substring(idx+1);
}
}
prepared.setObject(c, value == null ? null : SqlTypeSerializer.deserialize(value, fields.get( names.get(c-1)) ));
}
prepared.execute();
}
} catch (SQLException s) {
throw new IllegalStateException("Could not load memory table: ", s);
} catch (IOException s) {
throw new IllegalStateException("Could not load memory table: ", s);
} finally {
this.close(statement);
this.close(prepared);
}
}
/**
* This will export all records of the specified table and serialize it to the print writer.
*
* @param connection The connection to use for exporting
* @param out The writer for writing the serialized data
*/
public void save (Connection connection, PrintWriter out) {
Statement statement = null;
try {
statement = connection.createStatement();
ResultSet set = statement.executeQuery( "SELECT * FROM " + tableName);
set.beforeFirst();
Map<String, String> columns = this.getColumnData(set);
int c = 0;
out.println("VERSION:2");
for (String name : columns.keySet()) {
if (c++ > 0) out.print(",");
out.print(name + ":" + columns.get(name));
}
out.print("\n");
while (set.next()) {
c = 0;
for (String name : columns.keySet()) {
if (c++ > 0) out.print(",");
String encoded = SqlTypeSerializer.serialize(set.getObject(name));
out.print( new String( Base64.encode(encoded.getBytes()) ) );
}
out.print("\n");
}
} catch (SQLException s) {
throw ThrowableManagerRegistry.caught(new IllegalStateException("Could not save memory table: ", s));
} finally {
this.close(statement);
}
}
/**
* @param set The set for which to get column data
* @return A map, keys are the name of the column and the value is the name of the corresponding class
* @throws SQLException
*/
public Map<String, String> getColumnData (ResultSet set) throws SQLException {
Map<String, String> cd = new LinkedHashMap<String, String>();
ResultSetMetaData md = set.getMetaData();
for (int c=1; c<=md.getColumnCount(); c++) {
cd.put(md.getColumnName(c), md.getColumnClassName(c));
}
return cd;
}
/**
* @param st The statement to close
*/
public void close (Statement st) {
if (st != null) try { st.close(); } catch (SQLException e) {
ThrowableManagerRegistry.caught(e);
}
}
}