Advertisement
If you have a new account but are having problems posting or verifying your account, please email us on hello@boards.ie for help. Thanks :)
Hello all! Please ensure that you are posting a new thread or question in the appropriate forum. The Feedback forum is overwhelmed with questions that are having to be moved elsewhere. If you need help to verify your account contact hello@boards.ie

handling file writes across multiple clusters in java and first draft solution

Options
  • 24-11-2008 1:13pm
    #1
    Registered Users Posts: 1,269 ✭✭✭


    Hi there, I've been given a task in my company to introduce multiple thread and process handling concerning file creation in a system that can be deployed over multiple clusters. It's a web app that can be deployed on more than one cluster, however the file location is configured to exist on one server only. Because there are two seperate ways concurrency can occur (multiple threads on same VM, or multiple processes on different VM's, I've introduced handling for both scenarios). I'm not that familiar with threading (only ever did simple examples way back in college) so thoughts/recommendations on my first draft solution would be much appreciated.
    Also any ideas on writing tests to hit it with mutiple threads in same VM, and different processes at the same time would be great!

    Firstly here's the class that handles the File reference, locking and Semaphores.
    public class JavaSrcFilePool {
       
       //declared static to allow global access for different threads. 
       //Set as Binary Semaphore i.e. only 2 possible states available or unavailable.			   
       private final static Semaphore available = new Semaphore(1);
       private FileChannel channel;
       private FileLock lock;
       protected HashMap map = new HashMap();		  
    	
       /**
        * retrieve file reference if permit is available otherwise wait.	   
        **/
       public File getFileHandle(File key) throws InterruptedException { 
         available.acquire();
         return getFile(key);
       }
     
       /**
        * release permit on semaphore,unlock FileChannel
        **/
       public void releaseFileHandle(Object key) { // no synch
         if (releaseFile(key)){
           available.release();
     	try {
    	  lock.release();
    	} catch (IOException e) {
    	  // TODO Auto-generated catch block
    	  e.printStackTrace();
    	}	
       }		  
       
       /**
        * synchronized method to retrieve File reference from HashMap, if none exists already add one.
        **/
       protected synchronized File getFile(File key) throws InterruptedException { 
         File file = (File)map.get(key);
         File monitor = ((file == null) ? null : file);
         if (monitor == null) {
    	monitor = key;
    	map.put(monitor, monitor);
         }
         try {	
           if(channel == null){
             channel = new RandomAccessFile((File)monitor, "rw").getChannel();
    	}
          } catch (FileNotFoundException e) {
              // TODO Auto-generated catch block
    	  e.printStackTrace();
          }
          if(channel != null){
            boolean flag = true;
            while (flag) {
              try {
    	    lock = channel.tryLock();
    	    if(lock != null){
    	      flag = false;
    	    }
    	  } catch (Exception ex) {
    	      // TODO Auto-generated catch block
    	      ex.printStackTrace();
    	      Thread.sleep(1000);
    	    }								
    	  }
    	}
    	return (File)monitor;		      
          }
          
          /**
           * If file reference exists in HashMap remove it.
           **/
          protected synchronized boolean releaseFile(Object key) { 
            if(map.containsKey(key)){
              map.remove(key);
    	  return true;
            }
    	else
    	  return false;
          }
     
          /**
           * Accessor to allow write to File.
           **/
          public FileChannel getChannel() {
            return channel;
          }
     
    }
    
    

    And the calling code in RulesCompiler:
    public void compile(String fullName, String javaSource)
                throws EventsException {
     
    .....
     
    JavaSrcFilePool sourcePool = new JavaSrcFilePool();		        		        
    File javaSrcFile = null;
    try {
      //retrieve unique file object to use for synchronization. (If we created a new 
      //File object here it would just be a local instance for this thread)
      javaSrcFile = sourcePool.getFileHandle(new File(destDir, "./" + className + DOT_JAVA));
    } catch (InterruptedException ex) {
        //This is an insurance policy against deadlock caused by another
        CAT.error("Interrupted by another thread, releasing file", ex);
        sourcePool.releaseFileHandle(javaSrcFile);
    }
    catch (Exception ex) {
      //If some unknown exception occurs need to make sure file instance is released
      //for use by any waiting threads.
      CAT.error("Unknown exception occurred, releasing file", ex);
      sourcePool.releaseFileHandle(javaSrcFile);
    }
     
    //synchronize returned file object reference to prevent any other thread from accessing it.
    synchronized (javaSrcFile) {
    	
      try {
        ByteBuffer src = ByteBuffer.wrap(javaSource.getBytes());
        //write file 
        sourcePool.getChannel().write(src);
        //new file has been written so release lock and Semaphore
        sourcePool.releaseFileHandle(javaSrcFile);
        //update modified time stamp. If next calling thread version time stamp equals this, no need to write new file
        javaSrcFile.setLastModified(getVersionToCompile().getLastUpdateDate().getTime().getTime());
      } catch (IOException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
          sourcePool.releaseFileHandle(javaSrcFile);
      }
     
    .....
    } //end method
    


    I've also written a simple test to see how it handles a number of threads. I've just stepped through the code using the Eclipse debugger, each thread seems to behave as expected, if the Semaphore is unavailable they wait until it becomes available.

    This is it below:
    someMethod(..){
      TestThread tr1 = new TestThread(rule,version);
                        
      for (int i=0;i<5;i++){
        new Thread(tr1).start();
      }
    }
     
    class TestThread extends Thread {
      ExpressionRule rule;
      RuleVersion version;
      
      TestThread(ExpressionRule rule, RuleVersion version) {
        super();
        this.rule = rule;
        this.version = version;
      }
     
      public void run() {
        RulesCompiler compiler = new RulesCompiler();
        compiler.setVersionToCompile(version);
        try {
          compiler.compile(version.getJavaClassName(), JavaGenerator.generateSource(rule, version));
        } catch (BOException e) {
    	// TODO Auto-generated catch block
    	e.printStackTrace();
        } catch (Exception e) {
            // TODO Auto-generated catch block
    	e.printStackTrace();
        }
      }
    


    So is this a correct implementation? It seems to work, at least for multiple threads within the same VM. I still need to write decent testcases for both possibilities so suggestions on improving the admittedly rudimentary test above as well as tests for multiple vm access appreciated.

    Is there any way to improve the performance? Is it necessary for a sychronized getFile and releaseFile method? From my understanding of threading this would appear to be necessary to prevent any other thread managing to retrieve the file reference or to remove it while it's still in use by another thread. Would volatile and ConcurrentHashMap be something worth looking at?
    Any suggestions on alternatives to the way I've done it would also be appreciated. As I've said this is my first crack at handling multiple thread handling so would be interested in seeing other solutions as well as ways of improving my current one.

    Cheers


Advertisement