package dmg.util.cdb ; import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Hashtable; import dmg.cells.services.login.UserHandle; public class CdbDirectoryContainer extends CdbGLock implements CdbContainable, CdbElementable { private Class<CdbFileRecord> _elementClass; private Class<UserHandle> _handlerClass; private File _containerDirectory; private Constructor<UserHandle> _handlerConstructor; private Constructor<CdbFileRecord> _elementConstructor; private Method _elementRemoveMethod; private boolean _sticky; private boolean _exists = true ; private static final Class<?> [] __elementConstructorArguments = { CdbLockable.class , File.class , Boolean.TYPE } ; private static final Class<?> [] __handlerConstructorArguments = { String.class , CdbContainable.class , CdbElementable.class } ; private final Hashtable<String, ElementEntry> _table = new Hashtable<>() ; private static class ElementEntry { private CdbLockable _lockable; private int _refCounter; private ElementEntry( CdbLockable lockable ){ _lockable = lockable ; _refCounter = 1 ; } private CdbLockable getLockable(){ return _lockable ; } private synchronized void incrementRefCounter(){ _refCounter ++ ; } private synchronized void decrementRefCounter(){ _refCounter -- ; } private int getRefCounter(){ return _refCounter ; } } public CdbDirectoryContainer( CdbLockable superLock , Class<CdbFileRecord> elementClass , Class<UserHandle> handlerClass , File directory , boolean create ) throws CdbException { _elementClass = elementClass ; _handlerClass = handlerClass ; _containerDirectory = directory ; // // do the File stuff // // try{ if( create ){ if( _containerDirectory.exists() ) { throw new IllegalArgumentException("DataSource already exists"); } if( ! _containerDirectory.mkdir() ) { throw new IllegalArgumentException( "Failed to create : " + _containerDirectory); } }else{ if( ! _containerDirectory.isDirectory() ) { throw new IllegalArgumentException("DataSource does not exist"); } } // }catch( IOException ioe ){ // throw new CdbException( "IO Problem : "+ioe ) ; // } // // check for the existence of the neccessary // container constructors and remove method. // try{ _elementConstructor = elementClass.getConstructor( __elementConstructorArguments ) ; }catch( NoSuchMethodException nsme ){ throw new CdbException( "No matching container constructor found for : "+ elementClass.getName() ) ; } // // // try{ _elementRemoveMethod = _elementClass.getMethod("remove"); }catch( NoSuchMethodException nsmei ){ throw new CdbException( "No matching remove method found" ) ; } // // check for the existence of the handler constructor // try{ _handlerConstructor = _handlerClass.getConstructor( __handlerConstructorArguments ) ; }catch( NoSuchMethodException nsme ){ throw new CdbException( "No matching handler constructor found" ) ; } } public void setSticky( boolean sticky ){ _sticky = sticky ; } public CdbElementHandle createElement( String name ) throws CdbException, InterruptedException { // // make sure we are holding the mutex. // CdbElementHandle handle; // // // // open( CdbLockable.WRITE ) ; // // check for the existence of the resource ... // File file = new File( _containerDirectory , name ) ; if( file.exists() ){ close(CdbLockable.ABORT) ; throw new CdbException("Record already exists "+file ) ; } // // try{ handle = createMirrorEntry( name , true ) ; }catch( CdbException edbe ){ close(CdbLockable.ABORT) ; throw edbe ; } close(CdbLockable.COMMIT) ; return handle ; } public CdbElementHandle getElementByName( String name ) throws CdbException, InterruptedException { // // check for an entry in the cache // ElementEntry entry; CdbElementHandle handle; // // get the read mutex // open( CdbLockable.READ ) ; // // try to find the entry in the cache // if( ( entry = _table.get( name )) != null ){ // // it was still in the cache, so we only have to increment // the reference counter and that's it. // entry.incrementRefCounter() ; close(CdbLockable.COMMIT) ; return newHandlerInstance( name , this , entry.getLockable() ) ; } // // not in cache , look for it in the database itself ( filesystem ) // check for the existence of the resource ... // File file = new File( _containerDirectory , name ) ; if( ! file.exists() ){ // // doens't exists, so what says zap // close(CdbLockable.ABORT) ; throw new CdbException("Record does not exists "+file ) ; } // // try{ handle = createMirrorEntry( name , false ) ; }catch( CdbException edbe ){ close(CdbLockable.ABORT) ; throw edbe ; } close(CdbLockable.COMMIT) ; return handle ; } private CdbElementHandle createMirrorEntry( String name , boolean create ) throws CdbException { CdbElementHandle handle; CdbLockable element = null ; // // and create our 'mirror object' // try{ if( _elementConstructor != null ){ Object [] args = { this , new File(_containerDirectory , name ) , create } ; element = _elementConstructor.newInstance( args ); } }catch( InvocationTargetException ite ){ throw new CdbException( "Invocation Failed : "+ite.getTargetException() ) ; }catch( Exception e ){ throw new CdbException( "Problem : "+e ) ; } // // create the first handle // handle = newHandlerInstance( name , this , element ) ; // // and add it to our list. ( no need to increment ref counter, // this is done automatically on creation of entry ) // synchronized( _table ){ _table.put(name , new ElementEntry(element)) ; } // // return the write access // return handle ; } private CdbElementHandle newHandlerInstance( String name , CdbContainable container , CdbLockable element ) throws CdbException { CdbElementHandle handle; try{ Object [] args = { name , container , element } ; handle = _handlerConstructor.newInstance( args ); }catch( InvocationTargetException ite ){ throw new CdbException( "Invocation Failed : "+ite.getTargetException() ) ; }catch( Exception e ){ throw new CdbException( "Problem : "+e ) ; } return handle ; } public void removeElement( String name ) throws CdbException, InterruptedException { open( CdbLockable.WRITE ) ; CdbElementHandle handle = getElementByName( name ) ; try{ handle.open( CdbLockable.WRITE ) ; try{ ElementEntry entry = _table.get( name ); CdbLockable element = entry.getLockable() ; _elementRemoveMethod.invoke( element) ; }catch( InvocationTargetException ive ){ handle.close(CdbLockable.ABORT) ; Throwable t = ive.getTargetException() ; if( t instanceof CdbException ) { throw (CdbException) t; } throw new CdbException( "Problem in remove method : "+t ); }catch( Exception ee ){ handle.close(CdbLockable.ABORT) ; throw new CdbException( "Problem in remove method : "+ee); } handle.close(CdbLockable.COMMIT ) ; }catch( CdbException | InterruptedException edbe ){ close( CdbLockable.ABORT ) ; throw edbe ; } close( CdbLockable.COMMIT ) ; } @Override public synchronized void unlinkElement( String name ) { ElementEntry entry = _table.get( name ); if( entry == null ) { return; } entry.decrementRefCounter() ; if( ( ! _sticky ) && ( entry.getRefCounter() <= 0 ) ){ // System.out.println( name + " removed from hashtable" ) ; _table.remove( name ) ; } } public String [] getElementNames(){ return _containerDirectory.list() ; } // // this removes the container resource itself // @Override public synchronized void open( int mode ) throws CdbLockException, InterruptedException { if( ! _exists ) { throw new CdbLockException("Object removed"); } super.open( mode ) ; } @Override public void remove() throws CdbException { _exists = false ; if( ! _containerDirectory.delete() ) { throw new CdbException("Couldn't remove : " + _containerDirectory); } } @Override public void readLockGranted() { // System.out.println( "readLockGranted "+_containerDirectory ) ; } @Override public void writeLockGranted(){ // System.out.println( "writeLockGranted "+_containerDirectory ) ; } @Override public void readLockReleased(){ // System.out.println( "readLockReleased "+_containerDirectory ) ; } @Override public void writeLockReleased(){ // System.out.println( "writeLockReleased "+_containerDirectory ) ; } @Override public void writeLockAborted(){ // System.out.println( "writeLockAborted "+_containerDirectory ) ; } }