/*
* Copyright (C) 2009 eXo Platform SAS.
*
* This 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 software 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 software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.exoplatform.services.jcr.impl.storage.value;
import org.exoplatform.services.jcr.impl.storage.value.fs.operations.ValueLockSupport;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Created by The eXo Platform SAS.
*
* <br>
*
* This is singleton object for resources holding.
*
* <br>
* Date: 01.04.2009
*
* @author <a href="mailto:peter.nedonosko@exoplatform.com.ua">Peter Nedonosko</a>
* @version $Id: ValueDataResourceHolder.java 34801 2009-07-31 15:44:50Z dkatayev $
*/
public class ValueDataResourceHolder
{
/**
* Resources map.
*/
protected final Map<Object, VDResource> resources = new ConcurrentHashMap<Object, VDResource>();
/**
* ValueData resource holder.
*
*/
class VDResource
{
/**
* User Thread.
*/
final Thread user;
/**
* Per-JVM lock.
*/
final Object lock;
/**
* Lock support.
*/
final ValueLockSupport lockSupport;
/**
* Same user locks count.
*/
int userLocks = 0;
/**
* VDResource constructor.
*
* @param user Thread
* @param lock Object
* @param lockSupport ValueLockSupport
* @throws IOException if lock error occurs
*/
VDResource(Thread user, Object lock, ValueLockSupport lockSupport) throws IOException
{
this.user = user;
this.lock = lock;
this.lockSupport = lockSupport;
lockSupport.lock();
userLocks = 1;
}
/**
* Add user lock.
*
* Lock can be added by only the lock creator user (Thread recorded in user variable).
*
* @param user
* Thread
* @param lockSupport
* ValueLockSupport
* @return boolean, true if the resource reaquired by the same user
* @throws IOException
* if share is not supported
*/
boolean addUserLock(Thread user, ValueLockSupport lockSupport) throws IOException
{
if (this.user.equals(user))
{
lockSupport.share(this.lockSupport);
userLocks++;
return true;
}
return false;
}
/**
* Remove user lock.
*
* Lock can be removed by only the lock creator user (Thread recorded in user variable).
*
* @param user
* Thread
* @return boolean true - if no more locks of this user on the resource, false - otherwise
*/
boolean removeUserLock(Thread user)
{
if (this.user.equals(user))
{
userLocks--;
return userLocks <= 0;
}
return false;
}
}
/**
* Aquire ValueData resource.
*
* @param resource
* Object
* @param lockHolder
* ValueLockSupport
* @throws InterruptedException
* if resource lock is interrupted
* @return boolean, false - if the resource reaquired by the same user (Thread), true otherwise
* @throws IOException
* if lock error occurs
*/
public boolean aquire(final Object resource, final ValueLockSupport lockHolder) throws InterruptedException,
IOException
{
final Thread myThread = Thread.currentThread();
final VDResource res = resources.get(resource);
if (res != null)
{
if (res.addUserLock(myThread, lockHolder))
// resource locked in this thread (by me)
return false;
synchronized (res.lock)
{
// resource locked, wait for unlock
res.lock.wait();
// new record with existing lock (to respect Object.notify())
resources.put(resource, new VDResource(myThread, res.lock, lockHolder));
}
}
else
resources.put(resource, new VDResource(myThread, new Object(), lockHolder));
return true;
}
/**
* Release resource.
*
* @param resource
* Object
* @return boolean, true - if resource is released, false - if the resource still in use.
* @throws IOException
* if unlock error occurs
*/
public boolean release(final Object resource) throws IOException
{
final Thread myThread = Thread.currentThread();
final VDResource res = resources.get(resource);
if (res != null)
{
if (res.removeUserLock(myThread))
{
synchronized (res.lock)
{
// unlock holder
res.lockSupport.unlock();
// locked by this thread (by me)
// ...Wakes up a single thread that is waiting on this object's monitor
res.lock.notify();
// resources will be reputed with new VDResource in aquire() of another Thread
resources.remove(resource);
}
return true;
}
}
return false;
}
}