/*
* $Id: DBEntity.java,v 1.3 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.PermissionCollection;
import java.security.Permissions;
import java.security.Permission;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import anvil.core.Any;
import anvil.core.Serialization;
import anvil.core.UnserializationException;
import anvil.java.util.BindingEnumeration;
import anvil.java.util.Hashlist;
import anvil.java.security.PermissionCollectionCombiner;
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 abstract class DBEntity
{
protected static final int E_REMOVED = 1;
protected static final int E_RESTORED = 2;
protected static final int E_COMBINED = 4;
protected static final int E_DIRTY_ATTRIBUTES = 8;
protected static final int E_DIRTY_PERMISSIONS = 16;
protected static final int E_DIRTY = E_DIRTY_ATTRIBUTES | E_DIRTY_PERMISSIONS;
protected DBRealm _realm;
protected int _id;
protected String _name;
protected byte _flags = 0;
protected LinkList _parents = new LinkList();
protected PermissionCollection _collection = null;
protected PermissionCollection _combined = null;
protected ArrayList _permissions = new ArrayList();
protected Hashlist _attributes = null;
/*package*/ DBEntity(DBRealm realm, int id)
{
_realm = realm;
_id = id;
}
/*package*/ DBEntity(DBRealm realm, int id, String name)
{
_realm = realm;
_id = id;
_name = name;
}
protected void check()
{
if ((_flags & E_REMOVED) != 0) {
throw new IllegalStateException(this + " is removed");
}
if ((_flags & E_RESTORED) == 0) {
_flags |= E_RESTORED;
try {
restore();
} catch (OperationFailedException e) {
_realm.log().error(e);
throw new RuntimeException(e.toString());
}
}
}
protected void removed()
{
_flags |= E_REMOVED;
}
protected void dirty(int mask)
{
_flags |= mask;
}
public int hashCode()
{
return _id;
}
public boolean equals(Object o)
{
if (o instanceof DBEntity) {
return _id == ((DBEntity)o)._id;
}
return false;
}
public Realm getRealm()
{
return _realm;
}
protected abstract String getType();
/*package*/ int getId()
{
return _id;
}
public String getName()
{
return _name;
}
/*package*/ synchronized void setName(String name)
{
check();
_name = name;
}
protected String getCredentials()
{
return null;
}
public synchronized Tribe[] getParents()
{
check();
return (Tribe[]) _parents.toArray(Tribe.class);
}
public synchronized void addPermission(Permission perm)
{
dirty(E_DIRTY_PERMISSIONS);
_permissions.add(perm);
}
protected void addInitialPermission(Permission perm)
{
_permissions.add(perm);
if (_collection == null) {
_collection = new Permissions();
}
_collection.add(perm);
}
public synchronized void removePermission(Permission perm)
{
check();
dirty(E_DIRTY_PERMISSIONS);
_permissions.remove(perm);
}
public synchronized Iterator listPermissions()
{
check();
return _permissions.iterator();
}
public synchronized PermissionCollection getPermissions()
{
check();
return _collection;
}
private void getCombinedPermissions0(PermissionCollectionCombiner combiner,
DBEntity entity)
{
combiner.combine(entity.getPermissions());
Tribe[] tribes = entity.getParents();
int n = tribes.length;
for(int i=0; i<n; i++) {
getCombinedPermissions0(combiner, (DBEntity)tribes[i]);
}
}
public synchronized PermissionCollection getCombinedPermissions()
{
check();
if ((_flags & E_COMBINED)==0) {
_flags |= E_COMBINED;
PermissionCollectionCombiner combiner = new PermissionCollectionCombiner();
getCombinedPermissions0(combiner, this);
_combined = combiner.getResult();
}
return _combined;
}
public synchronized BindingEnumeration getVariables()
{
check();
if (_attributes == null) {
return BindingEnumeration.EMPTY;
} else {
return _attributes.keysAndElements();
}
}
public synchronized Any getVariable(String name)
{
check();
if (_attributes == null) {
return null;
}
return (Any)_attributes.get(name);
}
public synchronized Any setVariable(String name, Any value)
{
check();
if (_attributes == null) {
_attributes = new Hashlist();
}
dirty(E_DIRTY_ATTRIBUTES);
_attributes.put(name, value);
return value;
}
protected Any setInitialVariable(String name, Any value)
{
if (_attributes == null) {
_attributes = new Hashlist();
}
_attributes.put(name, value);
return value;
}
public synchronized Any checkVariable(String name)
{
check();
if (_attributes == null) {
return null;
}
return (Any)_attributes.get(name);
}
public synchronized boolean deleteVariable(String name)
{
check();
if (_attributes == null) {
return false;
}
if (_attributes.remove(name) != null) {
dirty(E_DIRTY_ATTRIBUTES);
return true;
}
return false;
}
public synchronized void commit() throws OperationFailedException
{
if ((_flags & E_DIRTY) == 0) {
return;
}
check();
_realm.perform(new Action() {
public Object perform(Connection conn) throws OperationFailedException, SQLException
{
boolean attrsDirty = (_flags & E_DIRTY_ATTRIBUTES) != 0;
boolean permissionsDirty = (_flags & E_DIRTY_PERMISSIONS) != 0;
/*remove permissions and attributes */ {
Statement stmt = null;
try {
stmt = conn.createStatement();
if (permissionsDirty) {
stmt.execute("delete from permission where id="+_id);
}
if (attrsDirty) {
stmt.execute("delete from attribute where id="+_id);
}
} finally {
close(stmt);
}
}
/* add permissions */
if (permissionsDirty && _permissions != null) {
PreparedStatement stmt = null;
try {
stmt = conn.prepareStatement("insert into permission values(?,?,?,?)");
int n = _permissions.size();
for(int i=0; i<n; i++) {
stmt.clearParameters();
stmt.setInt(1, _id);
Permission perm = (Permission)_permissions.get(i);
stmt.setString(2, perm.getClass().getName());
stmt.setString(3, perm.getName());
stmt.setString(4, perm.getActions());
stmt.execute();
}
} finally {
close(stmt);
}
}
/* add attributes */
if (attrsDirty) {
storeAttributes(conn);
}
return null;
}
});
}
protected void storeAttributes(Connection conn) throws OperationFailedException, SQLException
{
if (_attributes != null) {
PreparedStatement stmt = null;
try {
stmt = conn.prepareStatement("insert into attribute values(?,?,?,?,?)");
BindingEnumeration enum = _attributes.keysAndElements();
for(int i=0; enum.hasMoreElements(); i++) {
stmt.clearParameters();
String name = (String)enum.nextKey();
Any value = (Any)enum.nextElement();
stmt.setInt(1, _id);
stmt.setInt(2, i);
stmt.setString(3, name);
if(value.isString()) {
stmt.setInt(4, 1);
stmt.setString(5, value.toString());
} else {
stmt.setInt(4, 0);
ByteArrayOutputStream output = new ByteArrayOutputStream();
try {
Serialization.serialize(null, value, output);
} catch (IOException e) {
throw new OperationFailedException(
"Serizalition of type '"+value.classOf()+"' failed");
}
stmt.setBytes(5, output.toByteArray());
}
stmt.execute();
}
} finally {
Action.close(stmt);
}
}
}
public synchronized void remove() throws OperationFailedException
{
check();
synchronized(_realm) {
_realm.perform(new Action() {
public Object perform(Connection conn) throws SQLException
{
Statement stmt = conn.createStatement();
try {
stmt.executeQuery("delete from entity where id="+_id);
stmt.executeQuery("delete from permission where id="+_id);
stmt.executeQuery("delete from attribute where id="+_id);
stmt.executeQuery("delete from relation where child="+_id+" or parent="+_id);
return null;
} finally {
close(stmt);
}
}
});
removeRelations();
}
removed();
}
protected void removeRelations()
{
_parents.forEach(new LinkList.Action()
{
public void perform(Link link)
{
DBEntity entity = _realm.getCachedEntity(link.getId());
if (entity != null) {
entity.removeChild(DBEntity.this);
}
}
}
);
}
/*package*/ void create(final String[][] attrs) throws OperationFailedException
{
_realm.perform(
new Action() {
public Object perform(Connection conn) throws OperationFailedException, SQLException
{
PreparedStatement stmt = null;
try {
stmt = conn.prepareStatement("insert into entity values(?,?,?,?)");
stmt.setInt(1, _id);
stmt.setString(2, getType());
stmt.setString(3, _name);
stmt.setString(4, getCredentials());
stmt.execute();
} finally {
close(stmt);
}
if (attrs != null) {
int n = attrs.length;
for(int i=0; i<n; i++) {
String[] s = attrs[i];
setInitialVariable(s[0], Any.create(s[1]));
}
storeAttributes(conn);
}
return null;
}
}
);
}
/*package*/ void restore() throws OperationFailedException
{
_realm.perform(
new Action() {
public Object perform(Connection conn) throws OperationFailedException, SQLException
{
Statement stmt = null;
ResultSet set = null;
try {
stmt = conn.createStatement();
set = stmt.executeQuery("select * from permission where id="+_id);
while(set.next()) {
String type = set.getString(2);
String name = set.getString(3);
String actions = set.getString(4);
try {
Permission perm = PolicyPreferences.createPermission(
new String[] { type, name, actions });
addInitialPermission(perm);
} catch (Throwable t) {
_realm.log().error(t);
throw new OperationFailedException("Invalid permission: "+t.toString());
}
}
close(set);
set = stmt.executeQuery("select name, isstring, value from attribute where id="+_id);
while(set.next()) {
String name = set.getString(1);
if (set.getInt(2) != 0) {
setInitialVariable(name, Any.create(set.getString(3)));
} else {
byte[] data = set.getBytes(3);
try {
Any value = Serialization.unserialize(null, data, 0, data.length);
setInitialVariable(name, value);
} catch (UnserializationException t) {
throw new OperationFailedException("Invalid permission: "+t.toString());
}
}
}
close(set);
set = stmt.executeQuery("select parent, child, type from relation where parent="+_id+" or child="+_id);
while(set.next()) {
restoreRelation(set.getInt(1), set.getInt(2), set.getString(3).equals("t"));
}
return null;
} finally {
close(set);
close(stmt);
}
}
}
);
}
protected void restoreRelation(int parent, int child, boolean childIsTribe)
{
if (child == _id) {
_parents.add(new TribeLink(_realm, parent));
}
}
protected void addParent(DBTribe parent)
{
_parents.add(new TribeLink(_realm, parent.getId()));
}
protected void removeParent(DBTribe parent)
{
_parents.remove(parent.getId());
}
protected void removeChild(DBEntity child)
{
}
}