/*
*
* * Copyright 2014 Orient Technologies LTD (info(at)orientechnologies.com)
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
* *
* * For more information: http://www.orientechnologies.com
*
*/
package com.orientechnologies.common.concur.resource;
import com.orientechnologies.common.concur.lock.OLockException;
import com.orientechnologies.orient.core.OOrientShutdownListener;
import com.orientechnologies.orient.core.OOrientStartupListener;
import com.orientechnologies.orient.core.Orient;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Reentrant implementation of Resource Pool. It manages multiple resource acquisition on thread local map. If you're looking for a
* Reentrant implementation look at #OReentrantResourcePool.
*
* @author Andrey Lomakin (a.lomakin--at--orientechnologies.com)
* @author Luca Garulli (l.garulli--at--orientechnologies.com)
* @see OResourcePool
*/
public class OReentrantResourcePool<K, V> extends OResourcePool<K, V> implements OOrientStartupListener, OOrientShutdownListener {
private volatile ThreadLocal<Map<K, ResourceHolder<V>>> activeResources = new ThreadLocal<Map<K, ResourceHolder<V>>>();
private static final class ResourceHolder<V> {
private final V resource;
private int counter = 1;
private ResourceHolder(V resource) {
this.resource = resource;
}
}
public OReentrantResourcePool(final int maxResources, final OResourcePoolListener<K, V> listener) {
super(maxResources, listener);
Orient.instance().registerWeakOrientShutdownListener(this);
Orient.instance().registerWeakOrientStartupListener(this);
}
@Override
public void onShutdown() {
activeResources = null;
}
@Override
public void onStartup() {
if (activeResources == null)
activeResources = new ThreadLocal<Map<K, ResourceHolder<V>>>();
}
public V getResource(K key, final long maxWaitMillis, Object... additionalArgs) throws OLockException {
Map<K, ResourceHolder<V>> resourceHolderMap = activeResources.get();
if (resourceHolderMap == null) {
resourceHolderMap = new HashMap<K, ResourceHolder<V>>();
activeResources.set(resourceHolderMap);
}
final ResourceHolder<V> holder = resourceHolderMap.get(key);
if (holder != null) {
holder.counter++;
return holder.resource;
}
try {
final V res = super.getResource(key, maxWaitMillis, additionalArgs);
resourceHolderMap.put(key, new ResourceHolder<V>(res));
return res;
} catch (RuntimeException e) {
resourceHolderMap.remove(key);
// PROPAGATE IT
throw e;
}
}
public boolean returnResource(final V res) {
final Map<K, ResourceHolder<V>> resourceHolderMap = activeResources.get();
if (resourceHolderMap != null) {
K keyToRemove = null;
for (Map.Entry<K, ResourceHolder<V>> entry : resourceHolderMap.entrySet()) {
final ResourceHolder<V> holder = entry.getValue();
if (holder.resource.equals(res)) {
holder.counter--;
assert holder.counter >= 0;
if (holder.counter > 0)
return false;
keyToRemove = entry.getKey();
break;
}
}
resourceHolderMap.remove(keyToRemove);
}
return super.returnResource(res);
}
public int getConnectionsInCurrentThread(final K key) {
final Map<K, ResourceHolder<V>> resourceHolderMap = activeResources.get();
if (resourceHolderMap == null)
return 0;
final ResourceHolder<V> holder = resourceHolderMap.get(key);
if (holder == null)
return 0;
return holder.counter;
}
public void remove(final V res) {
this.resources.remove(res);
final List<K> activeResourcesToRemove = new ArrayList<K>();
final Map<K, ResourceHolder<V>> activeResourcesMap = activeResources.get();
if (activeResourcesMap != null) {
for (Map.Entry<K, ResourceHolder<V>> entry : activeResourcesMap.entrySet()) {
final ResourceHolder<V> holder = entry.getValue();
if (holder.resource.equals(res))
activeResourcesToRemove.add(entry.getKey());
}
for (K resourceKey : activeResourcesToRemove) {
activeResourcesMap.remove(resourceKey);
sem.release();
}
}
}
}