/*
* $Id: DBRealm.java,v 1.13 2002/09/16 08:05:06 jkl Exp $
*
* Copyright (c) 2002 Njet Communications Ltd. All Rights Reserved.
*
* Use is subject to license terms, as defined in
* Anvil Sofware License, Version 1.1. See LICENSE
* file, or http://njet.org/license-1.1.txt
*/
package anvil.server.db;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.security.Permission;
import java.util.ArrayList;
import anvil.Log;
import anvil.database.CannotReturnPooledConnectionException;
import anvil.database.NoConnectionPoolException;
import anvil.database.ConnectionManager;
import anvil.database.PooledConnection;
import anvil.core.Any;
import anvil.core.UnserializationException;
import anvil.core.Serialization;
import anvil.java.util.Hashlist;
import anvil.server.ConfigReader;
import anvil.server.Zone;
import anvil.server.Realm;
import anvil.server.Tribe;
import anvil.server.Citizen;
import anvil.server.Realm;
import anvil.server.Tribe;
import anvil.server.Citizen;
import anvil.server.OperationFailedException;
import anvil.server.RealmPreferences;
import anvil.server.PolicyPreferences;
public class DBRealm implements Realm
{
private Zone _zone;
private String _name = "auth";
private String _poolname;
private TribeLink _root = null;
private ConnectionManager _manager;
private Hashlist _entities = new Hashlist();
private int _cachesize = 100;
public DBRealm()
{
}
public void initialize(RealmPreferences prefs)
{
_zone = prefs.getParent();
_name = prefs.getName();
_poolname = (String)prefs.getPreference("pool");
if (_poolname == null) {
_zone.log().error("DBRealm: dbpool name not found from configuration, using default: auth");
_poolname = "auth";
}
_cachesize = ConfigReader.toInt((String)prefs.getPreference("cachesize"));
if (_cachesize < 32) {
_cachesize = 32;
}
try {
_manager = _zone.getManagerFor(_poolname);
} catch(NoConnectionPoolException e) {
_zone.log().error("DBRealm: error while getting ConnectionManager: " + e);
}
_zone.log().info("Realm " + this + " initialized");
}
public void stop()
{
_manager = null;
_name = null;
_poolname = null;
_root = null;
_entities.clear();
_entities = null;
_zone.log().info("Realm " + this + " stopped");
_zone = null;
}
/* package */ Log log()
{
return _zone.log();
}
public Citizen getAnonymousCitizen()
{
return null;
}
protected class GetAction extends Action
{
private String _name;
protected GetAction(String name)
{
_name = name;
}
public Object perform(Connection conn) throws OperationFailedException, SQLException
{
PreparedStatement stmt = null;
ResultSet set = null;
try {
stmt = conn.prepareStatement("select * from entity where name=?");
stmt.setString(1, _name);
set = stmt.executeQuery();
if (set.next()) {
return getEntity(set.getInt(1), set);
} else {
return null;
}
} finally {
close(set);
close(stmt);
}
}
}
public synchronized Citizen getCitizen(String username)
{
try {
Object o = perform(new GetAction(username));
if (o instanceof DBCitizen) {
return (DBCitizen)o;
}
} catch (OperationFailedException e) {
_zone.log().error(e);
}
return null;
}
public synchronized Tribe getTribe(String name)
{
try {
Object o = perform(new GetAction(name));
if (o instanceof DBTribe) {
return (DBTribe)o;
}
} catch (OperationFailedException e) {
_zone.log().error(e);
}
return null;
}
public synchronized Citizen[] searchCitizenByVariable(final String variable, final String value)
{
try {
final ArrayList results = new ArrayList();
perform(
new Action() {
public Object perform(Connection conn) throws OperationFailedException, SQLException
{
PreparedStatement stmt = null;
ResultSet set = null;
try {
stmt = conn.prepareStatement("select distinct entity.* from attribute,entity " +
"where attribute.name=? and attribute.isstring=1 and attribute.value=? and attribute.id=entity.id and entity.type='c'");
stmt.setString(1, variable);
stmt.setString(2, value);
set = stmt.executeQuery();
while(set.next()) {
results.add(getEntity(set.getInt(1), set));
}
return null;
} finally {
close(set);
close(stmt);
}
}
}
);
return (Citizen[]) results.toArray(new Citizen[results.size()]);
} catch (OperationFailedException e) {
_zone.log().error(e);
return null;
}
}
public Tribe createTribe(String name) throws OperationFailedException
{
DBTribe tribe = new DBTribe(this, getNextId(), name);
tribe.create(null);
cache(tribe);
return tribe;
}
public Citizen createCitizen(String username, String credentials)
throws OperationFailedException
{
DBCitizen citizen = new DBCitizen(this, getNextId(), username, credentials);
citizen.create(null);
cache(citizen);
return citizen;
}
public Citizen createCitizen(String username, String credentials, String[][] params)
throws OperationFailedException
{
DBCitizen citizen = new DBCitizen(this, getNextId(), username, credentials);
citizen.create(params);
cache(citizen);
return citizen;
}
public synchronized Tribe getRoot()
{
try {
if (_root == null) {
int id = ((Integer)perform(
new Action() {
public Object perform(Connection conn) throws SQLException
{
Statement stmt = null;
ResultSet set = null;
try {
int id = 0;
stmt = conn.createStatement();
set = stmt.executeQuery("select child from relation where parent=0");
if (set.next()) {
id = set.getInt(1);
}
return new Integer(id);
} finally {
close(set);
close(stmt);
}
}
}
)).intValue();
_root = new TribeLink(this, id);
}
return _root.getTribe();
} catch (OperationFailedException e) {
return null;
}
}
private DBTribe cast(Tribe tribe_)
{
if (!(tribe_ instanceof DBTribe)) {
throw new IllegalArgumentException("Parameter is not instanceof DBTribe");
}
DBTribe tribe = (DBTribe)tribe_;
if (tribe.getRealm() != this) {
throw new IllegalArgumentException("Given DBTribe instance is not from this realm");
}
return tribe;
}
public synchronized void setRoot(Tribe tribe_)
throws OperationFailedException
{
final DBTribe tribe = cast(tribe_);
perform(
new Action() {
public Object perform(Connection conn) throws SQLException
{
Statement stmt = null;
try {
stmt = conn.createStatement();
stmt.execute("delete from relation where parent=0");
stmt.execute("insert into relation values(0,"+tribe.getId()+",'t')");
return null;
} finally {
close(stmt);
}
}
}
);
_root = new TribeLink(this, tribe.getId());
}
/*package*/ Object perform(Action action) throws OperationFailedException
{
PooledConnection impl = null;
try {
impl = _manager.acquire(_poolname);
} catch (NoConnectionPoolException e) {
String msg = "Couldn't acquire connection: " + e;
_zone.log().error(e);
throw new OperationFailedException(msg);
} catch (CannotReturnPooledConnectionException e) {
String msg = "Couldn't acquire connection: " + e;
_zone.log().error(e);
throw new OperationFailedException(msg);
}
Connection conn = (Connection)impl.getConnection();
try {
/* conn.setAutoCommit(false) */
Object rv = action.perform(conn);
/* conn.commit(); */
return rv;
} catch (SQLException e) {
try {
/* conn.rollback(); */
conn.close();
} catch (Throwable t) {
}
_zone.log().error("SQL operation failed", e);
throw new OperationFailedException(e.toString());
} catch (OperationFailedException e) {
try {
/* conn.rollback(); */
conn.close();
} catch (Throwable t) {
}
_zone.log().error("Operation failed", e);
e.fillInStackTrace();
throw e;
} finally {
impl.release();
}
}
/*package*/ synchronized int getNextId() throws OperationFailedException
{
return ((Integer)perform(
new Action() {
public Object perform(Connection conn) throws SQLException
{
Statement stmt = null;
ResultSet set = null;
try {
int id = 1;
stmt = conn.createStatement();
set = stmt.executeQuery("select max(id) from entity");
if (set.next()) {
id = set.getInt(1);
}
return new Integer(id + 1);
} finally {
close(set);
close(stmt);
}
}
}
)).intValue();
}
/*package*/ synchronized DBEntity getCachedEntity(final int id)
{
if (id == 0) {
return null;
}
Integer key = new Integer(id);
DBEntity entity = (DBEntity)_entities.get(key);
if (entity != null) {
_entities.remove(key);
_entities.put(key, entity);
return entity;
}
return null;
}
/*package*/ synchronized DBEntity getEntity(final int id, ResultSet set)
throws OperationFailedException
{
if (id == 0) {
return null;
}
DBEntity entity = getCachedEntity(id);
if (entity != null) {
return entity;
}
if (set != null) {
try {
if (set.getString(2).equals("t")) {
return new DBTribe(DBRealm.this, id, set.getString(3));
} else {
return new DBCitizen(DBRealm.this, id, set.getString(3), set.getString(4));
}
} catch (SQLException e) {
throw new OperationFailedException(e.toString());
}
} else {
entity = (DBEntity)perform(
new Action() {
public Object perform(Connection conn) throws SQLException
{
Statement stmt = null;
ResultSet set = null;
try {
stmt = conn.createStatement();
set = stmt.executeQuery("select * from entity where id="+id);
if (set.next()) {
if (set.getString(2).equals("t")) {
return new DBTribe(DBRealm.this, id, set.getString(3));
} else {
return new DBCitizen(DBRealm.this, id, set.getString(3), set.getString(4));
}
} else {
return null;
}
} finally {
close(set);
close(stmt);
}
}
}
);
}
cache(entity);
return entity;
}
private void cache(DBEntity entity)
{
if (_entities.size() >= _cachesize) {
_entities.removeAt(0);
}
_entities.put(new Integer(entity.getId()), entity);
}
}