/*
* Jalisto - JAva LIght STOrage
* Copyright (C) 2000-2005 Xcalia http://www.xcalia.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*
* Xcalia
* 71, rue Desnouettes
* 75014 Paris - France
* http://www.xcalia.com
*/
package org.objectweb.jalisto.se.impl.meta;
import org.objectweb.jalisto.se.api.Session;
import org.objectweb.jalisto.se.api.query.FieldDescription;
import org.objectweb.jalisto.se.api.*;
import org.objectweb.jalisto.se.api.internal.InternalPhysicalFileAccess;
import org.objectweb.jalisto.se.api.internal.SessionInternal;
import org.objectweb.jalisto.se.api.internal.InternalMetaRepository;
import org.objectweb.jalisto.se.api.internal.InternalFactory;
import org.objectweb.jalisto.se.exception.JalistoException;
import org.objectweb.jalisto.se.exception.SchemaException;
import org.objectweb.jalisto.se.impl.server.IdentityProvider;
import org.objectweb.jalisto.se.impl.InFileAddress;
import org.objectweb.jalisto.se.impl.server.page.ClassPage;
import org.objectweb.jalisto.se.impl.trace.Trace;
import org.objectweb.jalisto.se.JalistoFactory;
import java.util.*;
public class InternalMetaRepositoryImpl implements InternalMetaRepository {
private InternalMetaRepositoryImpl(JalistoProperties properties) {
InternalFactory internalFactory = JalistoFactory.getInternalFactory();
tracer = internalFactory.getTracer(properties);
tracer.println(Trace.LOGICAL, "MetaRepositoryImpl : create new instance");
sessions = new ArrayList();
isMono = properties.isMonoImplementation();
oidProvider = internalFactory.getIdentityProvider(properties);
physicalAccess = internalFactory.getInternalPhysicalAccess(properties);
classPageSize = properties.getClassPageSize();
}
public void addSession(SessionInternal session) {
synchronized (sessions) {
if (isMono) {
sessions.clear();
}
if (!sessions.contains(session)) {
sessions.add(session);
}
}
}
public void removeSession(SessionInternal session) {
sessions.remove(session);
}
public Map getClassMetas() {
return classMetas;
}
public Map getClassTable() {
return classTable;
}
/**
* *************************** methods from MetaRepository ********************************
*/
public void checkNoActive(String message) {
for (short i = 0; i < sessions.size(); i++) {
if (((SessionInternal) sessions.get(i)).currentTransaction().isActive()) {
throw new SchemaException(message+((SessionInternal) sessions.get(i)).getSessionId());
}
}
}
public void checkNoSessions(String message) {
if (!sessions.isEmpty()) {
throw new JalistoException(message);
}
}
public void closeAllSessions() {
ArrayList checkedSessions = new ArrayList();
checkedSessions.addAll(sessions);
for (short i = 0; i < checkedSessions.size(); i++) {
SessionInternal s = (SessionInternal) sessions.get(i);
if (s.currentTransaction().isActive()) {
s.currentTransaction().rollback();
}
if (s.isOpen()) {
s.closeSession();
}
}
}
public void defineClass(Session session, ClassDescription classMetaDescription) {
try {
ClassDescription oldMeta = getMetaData(classMetaDescription.getClassName());
if (!oldMeta.equals(classMetaDescription)) {
throw new SchemaException(
"Class " + classMetaDescription.getClassName() + " is already define in base with other metadescription");
}
return;
} catch (SchemaException e) {
if (!e.getMessage().endsWith("is not define in base")) {
throw e;
}
}
session.currentTransaction().begin();
try {
Object clid = createClidFromClassName(classMetaDescription.getClassName());
writePersistentClassMetaDescription(clid, (ClassDescriptionImpl) classMetaDescription);
} catch (SchemaException fse) {
// class already define
}
session.currentTransaction().commit();
}
public ClassDescription getMetaData(String className) {
Object clid = getClidFromClassName(className);
return getPersistentClassMetaDescription(clid);
}
public void removeClass(Session session, String className) {
checkNoActive(MODIFY_DURING_TRANSACTION_MESSAGE);
Object sessionId = session.getInternalSession().getSessionId();
Object clid = classTable.get(className);
if (clid == null) {
throw new SchemaException("Class "+className+" is not define in base");
}
session.currentTransaction().begin();
Iterator extent =
session.getInternalSession().getOidTable().getFloidsFromClid(sessionId, clid, true).iterator();
while (extent.hasNext()) {
try {
session.deleteObjectByOid(extent.next());
} catch (Exception e) {
System.out.println("PROBLEM DURING CLEANING OF PERSISTENT INSTANCES");
e.printStackTrace();
}
}
classTable.remove(className);
InFileAddress ifa = (InFileAddress) clidIndex.remove(clid);
ClassDescriptionImpl meta = (ClassDescriptionImpl) classMetas.remove(clid);
physicalAccess.writeObjectInBase(META_REPOSITORY_IFA, classTable, true);
physicalAccess.writeObjectInBase(OID_IFA_CLID_INDEX_IFA, clidIndex, true);
ClassPage classPage = (ClassPage) physicalAccess.readFileObjectAt(ifa);
classPage.setDataAt(ifa, null);
physicalAccess.updateFileObject(classPage);
if (session.getQueryManager() != null) {
session.getQueryManager().getIndexManager().deleteIndexesOnClass(meta);
}
session.currentTransaction().commit();
}
public void addField(Session session, String className, FieldDescription fieldDescription) {
checkNoActive(MODIFY_DURING_TRANSACTION_MESSAGE);
session.currentTransaction().begin();
short index = fieldDescription.getIndex();
Object clid = getClidFromClassName(className);
ClassDescription meta = getPersistentClassMetaDescription(clid);
if (meta == null) {
throw new SchemaException("meta data of class " + className + " null in base");
}
if ((index < 0) || (index > meta.getNbrFields())) {
throw new SchemaException("index " + index + " out of range from 0 to " + meta.getNbrFields());
}
Iterator oids = session.getExtent(className).readFully().iterator();
while (oids.hasNext()) {
Object oid = oids.next();
Object[] o = session.readObjectByOid(oid);
Object[] oo = new Object[o.length + 1];
for (int i = 0; i < oo.length; i++) {
if (i < index) {
oo[i] = o[i];
} else if (i == index) {
oo[i] = null;
} else {
oo[i] = o[i - 1];
}
}
session.updateObjectByOid(oid, oo);
}
meta.addField(fieldDescription);
writePersistentClassMetaDescription(clid, meta);
session.currentTransaction().commit();
}
public String[] getFieldNames(String className) {
return getMetaData(className).getFieldNames();
}
public void renameField(Session session, String className, String oldFieldName, String newFieldName) {
checkNoActive(MODIFY_DURING_TRANSACTION_MESSAGE);
session.currentTransaction().begin();
Object clid = getClidFromClassName(className);
int index = getIndex(className, oldFieldName);
ClassDescription meta = getPersistentClassMetaDescription(clid);
if (meta == null) {
throw new SchemaException("meta data of class " + className + " null in base");
}
if ((index < 0) || (index > (meta.getNbrFields() - 1))) {
throw new SchemaException("index " + index + " out of range from 0 to " + (meta.getNbrFields() - 1));
}
String[] fieldNames = meta.getFieldNames();
fieldNames[index] = newFieldName;
writePersistentClassMetaDescription(clid, meta);
session.currentTransaction().commit();
}
public void removeField(Session session, String className, String fieldName) {
checkNoActive(MODIFY_DURING_TRANSACTION_MESSAGE);
session.currentTransaction().begin();
Object clid = getClidFromClassName(className);
int index = getIndex(className, fieldName);
ClassDescription meta = getPersistentClassMetaDescription(clid);
if (meta == null) {
throw new SchemaException("meta data of class " + className + " null in base");
}
if ((index < 0) || (index > (meta.getNbrFields() - 1))) {
throw new SchemaException("index " + index + " out of range from 0 to " + (meta.getNbrFields() - 1));
}
Iterator oids = session.getExtent(className).readFully().iterator();
while (oids.hasNext()) {
Object oid = oids.next();
Object[] o = session.readObjectByOid(oid);
Object[] oo = new Object[o.length - 1];
for (int i = 0; i < oo.length; i++) {
if (i < index) {
oo[i] = o[i];
} else {
oo[i] = o[i + 1];
}
}
session.updateObjectByOid(oid, oo);
}
meta.removeField(index);
writePersistentClassMetaDescription(clid, meta);
session.currentTransaction().commit();
}
public int getIndex(String className, String fieldName) {
String[] fieldNames = getMetaData(className).getFieldNames();
for (int i = 0; i < fieldNames.length; i++) {
if (fieldNames[i].equalsIgnoreCase(fieldName)) {
return i;
}
}
return -1;
}
public Collection getAllClassNames() {
return classTable.keySet();
}
/**
* ***************************** Page access methods ****************************************
*/
public ClassDescription getPersistentClassMetaDescription(Object clid) {
ClassDescriptionImpl meta = (ClassDescriptionImpl) classMetas.get(clid);
if (meta == null) {
meta = internalGetPersistentClassMetaDescription(clid);
classMetas.put(clid, meta);
}
return meta;
}
// allocation is done
public void writePersistentClassMetaDescription(Object clid, ClassDescription metaDescription) {
tracer.println(Trace.LOGICAL, "MetaRepositoryImpl : writePersistentClassMetaDescription({0}, {1})", clid,
metaDescription);
((ClassDescriptionImpl)metaDescription).setRepository(this);
// get ifa of the page where instance is stored
InFileAddress classIfa = (InFileAddress) clidIndex.get(clid);
ClassPage classPage = (ClassPage) physicalAccess.readFileObjectAt(classIfa);
classPage.setDataAt(classIfa, metaDescription);
physicalAccess.updateFileObject(classPage);
classMetas.put(clid, metaDescription);
}
// allocation is done
public void writePersistentClassMetaDescription(ClassDescriptionImpl metaDescription) {
Object clid = getClidFromClassName(metaDescription.getClassName());
writePersistentClassMetaDescription(clid, metaDescription);
}
private void allocateNewClidIfa(Object clid) {
tracer.println(Trace.LOGICAL, "MetaRepositoryImpl : allocateNewClidIfa({0})", clid);
if (currentAvalaibleClassPage.getIndex() == (classPageSize - 1)) {
currentAvalaibleClassPage = new InFileAddress(getNewClassPageAddress());
ClassPage classPage = new ClassPage(classPageSize);
classPage.setIfa(currentAvalaibleClassPage);
physicalAccess.insertFileObject(classPage);
} else {
currentAvalaibleClassPage.incrementeFileIndex();
}
clidIndex.put(clid, currentAvalaibleClassPage.getClone());
updateStateInBase(true);
physicalAccess.writeObjectInBase(OID_IFA_CLID_INDEX_IFA, clidIndex, true);
}
private ClassDescriptionImpl internalGetPersistentClassMetaDescription(Object clid) {
tracer.println(Trace.LOGICAL, "MetaRepositoryImpl : getPersistentClassMetaDescription({0})", clid);
// get ifa of the system page
InFileAddress classIfa = (InFileAddress) clidIndex.get(clid);
ClassPage classPage = (ClassPage) physicalAccess.readFileObjectAt(classIfa);
// get and return the page info
ClassDescriptionImpl classMetaDescription = (ClassDescriptionImpl) classPage.getDataAt(classIfa);
classMetaDescription.setRepository(this);
return classMetaDescription;
}
/**
* ************************************* CLID METHODS ********************************************
*/
public String getClassNameFromClid(Object clid) {
Iterator keys = classTable.keySet().iterator();
while (keys.hasNext()) {
Object key = keys.next();
if (classTable.get(key).equals(clid)) {
return (String) key;
}
}
return null;
}
public Object getClidFromClassName(String fullClassName) {
Object result = classTable.get(fullClassName);
if (result == null) {
throw new SchemaException("Class " + fullClassName + " is not define in base");
}
return result;
}
private String getNewClassPageAddress() {
return "cp" + (classPageIndex++);
}
private Object createClidFromClassName(String fullClassName) {
if (!classTable.containsKey(fullClassName)) {
Object newClid = oidProvider.getNewClid();
classTable.put(fullClassName, newClid);
allocateNewClidIfa(newClid);
updateStateInBase(true);
physicalAccess.writeObjectInBase(META_REPOSITORY_IFA, classTable, true);
return newClid;
}
throw new SchemaException("class " + fullClassName + " is already defined in base");
}
/**
* ******************************* INITIALIZATION ***************************************
*/
private void init(short classPageSize) {
classTable = new HashMap();
clidIndex = new Hashtable();
classPageIndex = 0;
currentAvalaibleClassPage = new InFileAddress(getNewClassPageAddress());
currentAvalaibleClassPage.setIndex(-1);
ClassPage firstClassPage = new ClassPage(classPageSize);
firstClassPage.setIfa(currentAvalaibleClassPage);
physicalAccess.insertFileObject(firstClassPage);
updateStateInBase(false);
physicalAccess.writeObjectInBase(META_REPOSITORY_IFA, classTable, false);
physicalAccess.writeObjectInBase(OID_IFA_CLID_INDEX_IFA, clidIndex, false);
classMetas = new HashMap();
}
private void getStateFromBase() {
classTable = (Map) physicalAccess.readFileObjectAt(META_REPOSITORY_IFA).getData();
clidIndex = (Map) physicalAccess.readFileObjectAt(OID_IFA_CLID_INDEX_IFA).getData();
Object[] datas = (Object[]) physicalAccess.readFileObjectAt(MR_STATE_IFA).getData();
currentAvalaibleClassPage = (InFileAddress) datas[0];
classPageIndex = ((Integer) datas[1]).intValue();
classMetas = new HashMap();
Iterator clids = classTable.values().iterator();
while (clids.hasNext()) {
Object clid = clids.next();
ClassDescription meta = getPersistentClassMetaDescription(clid);
classMetas.put(clid, meta);
}
}
public synchronized void updateStateInBase(boolean isUpdate) {
Object[] datas = new Object[2];
datas[0] = currentAvalaibleClassPage;
datas[1] = new Integer(classPageIndex);
physicalAccess.writeObjectInBase(MR_STATE_IFA, datas, isUpdate);
}
public static InternalMetaRepositoryImpl getAnMetaRepository(JalistoProperties properties) {
InternalMetaRepositoryImpl repository = new InternalMetaRepositoryImpl(properties);
try {
// load it from base
repository.getStateFromBase();
} catch (Exception e) {
repository.init(properties.getClassPageSize());
}
return repository;
}
private ArrayList sessions;
private IdentityProvider oidProvider;
private InternalPhysicalFileAccess physicalAccess;
private Map classMetas; // clid -> classMeta
private Map classTable; // classname -> clid
private Map clidIndex; // clid -> class meta's ifa
private InFileAddress currentAvalaibleClassPage;
private int classPageIndex;
private int classPageSize;
private boolean isMono;
private Trace tracer;
private static final InFileAddress META_REPOSITORY_IFA = new InFileAddress("metarep");
private static final InFileAddress OID_IFA_CLID_INDEX_IFA = new InFileAddress("clid");
private static final InFileAddress MR_STATE_IFA = new InFileAddress("mrstate");
public static final String MODIFY_DURING_TRANSACTION_MESSAGE = "Try to modify schema during active transaction : ";
}
|