I need to load Configuration mapping from the database. The configuration mapping is represented as
Hashtable<String, Hashtable<String,String>>
We cannot load the mapping on the start since we depend on another war application for database connection. So, the mapping is loaded on the first call to configuration in the class(singleton) ResourcesStorage
implementing ServletContextListener
. At the same time the reset JSP calls static reset()
method in this listener. I added synchronization using reentrant
lock. I am locking only resetting because different threads should be able to retrieve data simultaneously. However, this does not work well.
I am getting
IllegalMonitorStateException on notFull.await();
java.lang.IllegalMonitorStateException
at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:127)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.fullyRelease(AbstractQueuedSynchronizer.java:1606)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:1922)
Is my design correct?
import java.util.Hashtable;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class ResourcesStorage implements ServletContextListener {
private static ResourcesStorage instance;
protected static Log log = LogFactory.getLog(ResourcesStorage.class);
protected Hashtable<String, Hashtable<String,String>> DBsettings =new Hashtable <String, Hashtable<String,String>>();
private ServletContext context = null;
private boolean isLoading = false;
public synchronized boolean isLoading() {
return isLoading;
}
public synchronized void setLoading(boolean isLoading) {
this.isLoading = isLoading;
}
private Lock lock = new ReentrantLock();
private Condition notFull = lock.newCondition();
public void contextDestroyed(ServletContextEvent arg0) {}
public void contextInitialized(ServletContextEvent event) {
this.context = event.getServletContext();
//initialize the static reference _instance
instance=this;
reloadAllSettings();
}
public Hashtable<String, Hashtable<String,String>> getDBSettings()
{
return DBsettings;
}
public static String getSettings(String groupName, String keyName)
{
ResourcesStorage instance = ResourcesStorage.getInstance();
Hashtable<String, Hashtable<String,String>> dbsettings = instance.getDBSettings();
Hashtable<String,String> group =(Hashtable<String,String>) dbsettings.get(groupName);
if(group!=null && !group.isEmpty())
return group.get(keyName);
else
return null;
}
public String getValueSettings (String groupName, String keyName)
{
try
{
while (isLoading() == false)
{
notFull.await();
}
Hashtable<String,String> group =(Hashtable<String,String>) DBsettings.get(groupName);
if(group!=null && !group.isEmpty())
{
return group.get(keyName);
}
else
{
return null;
}
}
catch (Exception e)
{
log.error("getValueSetting", e);
}
return null;
}
public static void reSet(){
ResourcesStorage instance = ResourcesStorage.getInstance();
instance.reloadAllSettings();
}
public void reloadAllSettings(){
lock.lock();
setLoading(true);
try
{
// .....
//getting resources from the database
setLoading(false);
notFull.signal();
} catch (Exception e) {
//...
}
finally
{
lock.unlock();
}
}
public static ResourcesStorage getInstance()
{
return instance;
}
public ServletContext getContext() {
return context;
}
public void setContext(ServletContext context) {
this.context = context;
}
}
Suggestions:
Either the settings have been loaded or they haven't. If they have been loaded, use them.
If they haven't been loaded, call a synchronized method. This is your only required locking mechanism. In the method check to see if they have been loaded (due to race conditions), if they have, return. Otherwise go get them, while you block the other threads that are waiting.
Make sure you keep your settings reference in a volatile variable.
Edit:
My suggestions do not support having multiple threads loading the same data at the same time. This is intentional.