VolD 0.1

FileSystemDirectory.java

Go to the documentation of this file.
00001 
00002 package de.zib.vold.backend;
00003 
00004 import de.zib.vold.common.VoldException;
00005 
00006 import java.io.File;
00007 import java.io.FileFilter;
00008 import java.io.UnsupportedEncodingException;
00009 import java.io.IOException;
00010 
00011 import java.util.List;
00012 import java.util.LinkedList;
00013 import java.util.Map;
00014 import java.util.HashMap;
00015 
00016 import java.net.URLEncoder;
00017 import java.net.URLDecoder;
00018 
00019 import javax.annotation.PostConstruct;
00020 import javax.annotation.PreDestroy;
00021 
00022 import org.slf4j.Logger;
00023 import org.slf4j.LoggerFactory;
00024 
00038 public class FileSystemDirectory implements PartitionedDirectoryBackend
00039 {
00040         private File root;
00041         private String rootPath;
00042         protected final Logger log = LoggerFactory.getLogger( this.getClass() );
00043 
00044         private String enc = "utf-8";
00045 
00055         public FileSystemDirectory( String path, String enc )
00056         {
00057                 this.rootPath = path;
00058                 this.root = null;
00059                 this.enc = enc;
00060         }
00061 
00065         public FileSystemDirectory( )
00066         {
00067                 this.rootPath = null;
00068                 this.root = null;
00069         }
00070 
00078         public void setRootPath( String rootPath )
00079         {
00080                 if( isopen() )
00081                 {
00082                         log.warn( "Tried to change root path while database is open. Closing it before!" );
00083                         close();
00084                 }
00085 
00086                 this.rootPath = rootPath;
00087         }
00088 
00096         public void setEnc( String enc )
00097         {
00098                 if( isopen() )
00099                 {
00100                         log.warn( "Tried to change encoding while database is open. Closing it before! Nevertheless, this is a dangerous operation!" );
00101                         close();
00102                 }
00103 
00104                 this.enc = enc;
00105         
00106         }
00107 
00111         public void checkState( )
00112         {
00113                 if( null == rootPath )
00114                 {
00115                         throw new IllegalStateException( "Tried to operate on FileSystemDirectory while it had not been initialized yet. Set the rootPath before!" );
00116                 }
00117         }
00118 
00126         @Override
00127         @PostConstruct
00128         public void open( )
00129         {
00130                 // guard
00131                 {
00132                         checkState();
00133 
00134                         if( isopen() )
00135                         {
00136                                 log.warn( "Tried to open twice. Closing it before!" );
00137                                 close();
00138                         }
00139                 }
00140 
00141                 try
00142                 {
00143                         root = new File( rootPath );
00144                 }
00145                 catch( Exception e )
00146                 {
00147                         root = null;
00148                         throw new VoldException( e );
00149                 }
00150 
00151                 if( ! root.isDirectory() )
00152                 {
00153                         root = null;
00154                         throw new VoldException( "Directory could not be opened: " + rootPath + " is no directory!" );
00155                 }
00156 
00157                 log.info( "Backend opened." );
00158         }
00159 
00167         @Override
00168         @PreDestroy
00169         public void close( )
00170         {
00171                 if( ! isopen() )
00172                 {
00173                         log.warn( "Tried to close database while it wasn't open." );
00174                         return;
00175                 }
00176 
00177                 root = null;
00178 
00179                 log.info( "Backend closed." );
00180         }
00181 
00187         @Override
00188         public boolean isopen( )
00189         {
00190                 if( null == root || null == rootPath )
00191                         return false;
00192                 else
00193                         return true;
00194         }
00195 
00210         @Override
00211         public void insert( int partition, List< String > key, List< String > value )
00212         {
00213                 // guard
00214                 {
00215                         log.trace( "Insert: " + partition + ":'" + key.toString() + "' -> '" + value.toString() + "'" );
00216 
00217                         if( ! isopen() )
00218                         {
00219                                 throw new VoldException( "Tried to operate on closed database." );
00220                         }
00221                 }
00222 
00223                 List< String > d = _get_partition_dir( partition, key );
00224 
00225                 String path = _buildpath( d );
00226 
00227                 // create key (directory) and clear its content
00228                 {
00229                         File f = new File( path );
00230                         if( ! f.exists() )
00231                         {
00232                                 f.mkdirs();
00233                                 //log.debug( "Could not create directory '" + path + "'" );
00234                         }
00235 
00236                         for( File file: f.listFiles() )
00237                         {
00238                                 if( file.isFile() )
00239                                 {
00240                                         file.delete();
00241                                 }
00242                         }
00243                 }
00244 
00245                 // create all files
00246                 {
00247                         for( String filename: value )
00248                         {
00249                                 String filepath;
00250                                 try
00251                                 {
00252                                         filepath = path + "/" + _buildfile( filename );
00253                                 }
00254                                 catch( VoldException e )
00255                                 {
00256                                         throw new VoldException( "Error on insertion of value " + filename + " for key " + key.toString() + "(" + path + ").", e );
00257                                 }
00258 
00259                                 log.debug( "Creating value '" + filepath + "'" );
00260                                 File f = new File( filepath );
00261 
00262                                 try
00263                                 {
00264                                         f.createNewFile();
00265                                 }
00266                                 catch( IOException e )
00267                                 {
00268                                         throw new VoldException( "Error on insertion of value " + filename + " for key " + key.toString() + "(" + path + ").", e );
00269                                 }
00270                         }
00271                 }
00272         }
00273 
00282         @Override
00283         public void delete( int partition, List< String > key )
00284         {
00285                 // guard
00286                 {
00287                         log.trace( "Delete: " + partition + ":'" + key.toString() + "'" );
00288 
00289                         if( ! isopen() )
00290                         {
00291                                 throw new VoldException( "Tried to operate on closed database." );
00292                         }
00293                 }
00294 
00295                 List< String > d = _get_partition_dir( partition, key );
00296 
00297                 String path = _buildpath( d );
00298 
00299                 // clear content of directory (but not subdirectories!)
00300                 {
00301                         File f = new File( path );
00302 
00303                         if( ! f.exists() )
00304                         {
00305                                 log.warn( "FileSystemDirectory tried to delete nonexistent " + f.getAbsolutePath() );
00306                                 return;
00307                         }
00308 
00309                         for( File file: f.listFiles() )
00310                         {
00311                                 if( file.isFile() )
00312                                 {
00313                                         file.delete();
00314                                 }
00315                         }
00316                 }
00317 
00318                 // recursively delete directory, if it is empty now
00319                 {
00320                         while( d.size() != 0 )
00321                         {
00322                                 File dir = new File( path );
00323 
00324                                 // try to delete directory (must be empty for that...)
00325                                 if( ! dir.delete() )
00326                                         break;
00327 
00328                                 d.remove( d.size()-1 );
00329                                 path = _buildpath( d );
00330                         }
00331                 }
00332         }
00333 
00343         @Override
00344         public List< String > lookup( int partition, List< String > key )
00345         {
00346                 // guard
00347                 {
00348                         log.trace( "Lookup: " + partition + ":'" + key.toString() + "'" );
00349 
00350                         if( ! isopen() )
00351                         {
00352                                 throw new VoldException( "Tried to operate on closed database." );
00353                         }
00354                 }
00355 
00356                 List< String > d = _get_partition_dir( partition, key );
00357                 String path = _buildpath( d );
00358                 File f = new File( path );
00359 
00360                 if( ! f.exists() )
00361                 {
00362                         log.trace( " ... no results." );
00363                         return null;
00364                 }
00365 
00366                 List< String > result = new LinkedList< String >();
00367 
00368                 for( File file: f.listFiles() )
00369                 {
00370                         if( file.isFile() )
00371                         {
00372                                 try
00373                                 {
00374                                         result.add( buildfile( file.getName() ) );
00375                                 }
00376                                 catch( VoldException e )
00377                                 {
00378                                         log.warn( "Skipping file " + file.getName() + " while looking for " + key.toString() + ", since an error occured: " + e.getMessage() );
00379                                 }
00380                         }
00381                 }
00382 
00383                 // empty directorys (i.e. they contain no files!) are interpreted to not exist as key
00384                 if( 0 == result.size() )
00385                 {
00386                         log.trace( "... no results." );
00387                         return null;
00388                 }
00389 
00390                 log.trace( " results: " + result.toString() );
00391                 return result;
00392         }
00393 
00403         @Override
00404         public Map< List< String >, List< String > > prefixlookup( int partition, List< String > key )
00405         {
00406                 // guard
00407                 {
00408                         log.trace( "PrefixLookup: " + partition + ":" + key.toString() );
00409 
00410                         if( ! isopen() )
00411                         {
00412                                 throw new VoldException( "Tried to operate on closed database." );
00413                         }
00414                 }
00415 
00416                 Map< List< String >, List< String > > result = new HashMap< List< String >, List< String > >();
00417 
00418                 String prefix = _builddir( key.remove( key.size()-1 ) );
00419 
00420                 List< String > d = _get_partition_dir( partition, key );
00421 
00422                 String path = _buildpath( d );
00423 
00424                 File f = new File( path );
00425                 if( ! f.exists() )
00426                 {
00427                         log.trace( " ... no results." );
00428                         return result;
00429                 }
00430 
00431                 for( File file: f.listFiles( new PrefixFilter( prefix ) ) )
00432                 {
00433                         // potential candidate...
00434                         if( file.isDirectory() )
00435                         {
00436                                 List< String > k = new LinkedList< String >( key );
00437                                 k.add( builddir( file.getName() ) );
00438 
00439                                 try
00440                                 {
00441                                         recursive_add( k, path + "/" + file.getName(), result );
00442                                 }
00443                                 catch( VoldException e )
00444                                 {
00445                                         log.warn( "Skipping directory " + path + "/" + file.getName() + ", since an error occured: " + e.getMessage() );
00446                                 }
00447                         }
00448                         else
00449                         {
00450                                 try
00451                                 {
00452                                         file_add( key, file.getName(), result );
00453                                 }
00454                                 catch( VoldException e )
00455                                 {
00456                                         log.warn( "Skipping file " + file.getName() + " in recursive listing, since it has no valid format: " + e.getMessage() );
00457                                 }
00458                         }
00459                 }
00460 
00461                 log.trace( " results: " + result.toString() );
00462                 return result;
00463         }
00464 
00468         private void recursive_add( List< String > key, String dir, Map< List< String >, List< String > > map )
00469         {
00470                 File _dir = new File( dir );
00471 
00472                 if( ! _dir.isDirectory() )
00473                 {
00474                         throw new VoldException( "The path " + dir + " describes no directory, as expected" );
00475                 }
00476 
00477                 for( File file: _dir.listFiles() )
00478                 {
00479                         if( file.isDirectory() )
00480                         {
00481                                 List< String > k = new LinkedList< String >( key );
00482                                 try
00483                                 {
00484                                         k.add( builddir( file.getName() ) );
00485                                 }
00486                                 catch( VoldException e )
00487                                 {
00488                                         log.warn( "Skipping directory " + file.getName() + " in recursive listing, since it has no valid format: " + e.getMessage() );
00489                                 }
00490 
00491                                 recursive_add( k, dir + "/" + file.getName(), map );
00492                         }
00493                         else
00494                         {
00495                                 try
00496                                 {
00497                                         file_add( key, file.getName(), map );
00498                                 }
00499                                 catch( VoldException e )
00500                                 {
00501                                         log.warn( "Skipping file " + file.getName() + " in recursive listing, since it has no valid format: " + e.getMessage() );
00502                                 }
00503                         }
00504                 }
00505         }
00506 
00510         private void file_add( List< String > key, String value, Map< List< String >, List< String > > map )
00511         {
00512                 if( map.containsKey( key ) )
00513                 {
00514                         List< String > l = map.get( key );
00515                         l.add( buildfile( value ) );
00516                 }
00517                 else
00518                 {
00519                         List< String > v = new LinkedList< String >();
00520                         v.add( buildfile( value ) );
00521                         map.put( key, v );
00522                 }
00523         }
00524 
00528         private String _buildpath( List< String > dir )
00529         {
00530                 String path = new String( rootPath );
00531 
00532                 for( String d: dir )
00533                 {
00534                         path += "/" + d;
00535                 }
00536 
00537                 return path;
00538         }
00539 
00543         private List< String > _get_partition_dir( int partition, List< String > dir )
00544         {
00545                 List< String > l = new LinkedList< String >( );
00546 
00547                 l.add( String.valueOf( partition ) );
00548 
00549                 for( String d: dir )
00550                 {
00551                         try
00552                         {
00553                                 l.add( _builddir( d ) );
00554                         }
00555                         catch( VoldException e )
00556                         {
00557                                 throw new VoldException( "Could not determine Lowlevel directory of abstract directory " + partition + ":" + dir.toString() + ". ", e );
00558                         }
00559                 }
00560 
00561                 return l;
00562         }
00563 
00567         private String _buildfile( String dir )
00568         {
00569                 try
00570                 {
00571                         return "-" + URLEncoder.encode( dir, enc );
00572                 }
00573                 catch( UnsupportedEncodingException e )
00574                 {
00575                         throw new VoldException( "Could not encode directory name of " + dir + ".", e );
00576                 }
00577         }
00578 
00582         private String buildfile( String dir )
00583         {
00584                 try
00585                 {
00586                         String dec = URLDecoder.decode( dir, enc );
00587                         return dec.substring( 1, dec.length() );
00588                 }
00589                 catch( UnsupportedEncodingException e )
00590                 {
00591                         throw new VoldException( "Could not decode directory name of " + dir + ".", e );
00592                 }
00593         }
00594 
00598         private String _builddir( String dir )
00599         {
00600                 try
00601                 {
00602                         return "+" + URLEncoder.encode( dir, enc );
00603                 }
00604                 catch( UnsupportedEncodingException e )
00605                 {
00606                         throw new VoldException( "Could not encode directory name of " + dir + ".", e );
00607                 }
00608         }
00609 
00613         private String builddir( String dir )
00614         {
00615                 try
00616                 {
00617                         String dec = URLDecoder.decode( dir, enc );
00618                         return dec.substring( 1, dec.length() );
00619                 }
00620                 catch( UnsupportedEncodingException e )
00621                 {
00622                         throw new VoldException( "Could not decode directory name of " + dir + ".", e );
00623                 }
00624         }
00625 
00632         private class PrefixFilter implements FileFilter
00633         {
00634                 private final String prefix;
00635 
00636                 public PrefixFilter( String prefix )
00637                 {
00638                         this.prefix = prefix;
00639                 }
00640 
00641                 public boolean accept( File pathname )
00642                 {
00643                         return pathname.getName().substring( 0, prefix.length() ).equals( prefix );
00644                 }
00645         }
00646 
00647 }