/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package io.hops.transaction.context;
import com.google.common.base.Predicate;
import io.hops.exception.StorageCallPreventedException;
import io.hops.exception.StorageException;
import io.hops.exception.TransactionContextException;
import io.hops.metadata.common.FinderType;
import io.hops.metadata.hdfs.dal.LeasePathDataAccess;
import io.hops.metadata.hdfs.entity.LeasePath;
import io.hops.transaction.lock.TransactionLocks;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class LeasePathContext extends BaseEntityContext
<LeasePathContext.LeasePathPK, LeasePath> {
private final LeasePathDataAccess<LeasePath> dataAccess;
private final Map<Integer, Set<LeasePath>> hIdToLPsMap =
new HashMap<Integer, Set<LeasePath>>();
private final List<String> nullLPs = new ArrayList<String>();
public LeasePathContext(LeasePathDataAccess<LeasePath> dataAccess) {
this.dataAccess = dataAccess;
}
@Override
public void update(LeasePath hopLeasePath)
throws TransactionContextException {
super.update(hopLeasePath);
addToHIdToLPsMap(hopLeasePath);
if(isLogDebugEnabled()) {
log("added-lpath", "path", hopLeasePath.getPath(), "hid",
hopLeasePath.getHolderId());
}
}
@Override
public void remove(LeasePath hopLeasePath)
throws TransactionContextException {
super.remove(hopLeasePath);
removeFromHIdToLPsMap(hopLeasePath);
if(isLogDebugEnabled()) {
log("removed-lpath", "path", hopLeasePath.getPath(),
"holderId ", hopLeasePath.getHolderId());
}
}
@Override
public LeasePath find(FinderType<LeasePath> finder, Object... params)
throws TransactionContextException, StorageException {
LeasePath.Finder lFinder = (LeasePath.Finder) finder;
switch (lFinder) {
case ByPath:
return findByPath(lFinder, params);
}
throw new RuntimeException(UNSUPPORTED_FINDER);
}
@Override
public Collection<LeasePath> findList(FinderType<LeasePath> finder,
Object... params) throws TransactionContextException, StorageException {
LeasePath.Finder lFinder = (LeasePath.Finder) finder;
switch (lFinder) {
case ByHolderId:
return findByHolderId(lFinder, params);
case ByPrefix:
return findByPrefix(lFinder, params);
}
throw new RuntimeException(UNSUPPORTED_FINDER);
}
@Override
public void prepare(TransactionLocks tlm)
throws TransactionContextException, StorageException {
dataAccess.prepare(getRemoved(), getAdded(), getModified());
}
@Override
public void clear() throws TransactionContextException {
super.clear();
hIdToLPsMap.clear();
}
@Override
LeasePathPK getKey(LeasePath hopLeasePath) {
return new LeasePathPK(hopLeasePath.getPath(), hopLeasePath.getHolderId());
}
private LeasePath findByPath(LeasePath.Finder lFinder, Object[] params)
throws StorageCallPreventedException, StorageException {
final String path = (String) params[0];
LeasePath result = null;
if (containsInHIdToLPsMap(path)) {
result = getLPFromHIdToLPsMap(path);
hit(lFinder, result, "path", path);
}else if(nullLPs.contains(path)){
return null;
}else {
aboutToAccessStorage(lFinder, params);
result = dataAccess.findByPath(path);
if(result != null){
gotFromDBInternal(result);
}else{
nullLPs.add(path);
}
miss(lFinder, result, "path", path);
}
return result;
}
private Collection<LeasePath> findByHolderId(LeasePath.Finder lFinder,
Object[] params) throws StorageCallPreventedException, StorageException {
final int holderId = (Integer) params[0];
Collection<LeasePath> result = null;
if (hIdToLPsMap.containsKey(holderId)) {
result = new ArrayList<LeasePath>(hIdToLPsMap.get(holderId));
hit(lFinder, result, "hid", holderId);
} else {
aboutToAccessStorage(lFinder, params);
result = dataAccess.findByHolderId(holderId);
gotFromDBInternal(holderId, result);
miss(lFinder, result, "hid", holderId);
}
return result;
}
private Collection<LeasePath> findByPrefix(LeasePath.Finder lFinder,
Object[] params) throws StorageCallPreventedException, StorageException {
final String prefix = (String) params[0];
Collection<LeasePath> result = null;
try {
aboutToAccessStorage(lFinder, params);
result = dataAccess.findByPrefix(prefix);
gotFromDBInternal(result);
miss(lFinder, result, "prefix", prefix, "numOfLps", result.size());
} catch (StorageCallPreventedException ex) {
// This is allowed in querying lease-path by prefix, this is needed in delete operation for example.
result = getFilteredByPrefix(prefix);
hit(lFinder, result, "prefix", prefix, "numOfLps", result.size());
}
return result;
}
private Collection<LeasePath> getFilteredByPrefix(final String prefix) {
return get(new Predicate<ContextEntity>() {
@Override
public boolean apply(ContextEntity input) {
if (input.getState() != State.REMOVED) {
LeasePath leasePath = input.getEntity();
if (leasePath != null) {
return leasePath.getPath().contains(prefix);
}
}
return false;
}
});
}
void gotFromDBInternal(LeasePath leasePath) {
if(leasePath != null){
super.gotFromDB(new LeasePathContext.LeasePathPK(leasePath.getPath(),
leasePath.getHolderId()), leasePath);
addToHIdToLPsMap(leasePath);
}
}
void gotFromDBInternal(Collection<LeasePath> entityList) {
if(entityList != null && !entityList.isEmpty()){
for(LeasePath lp : entityList){
gotFromDBInternal(lp);
}
}
}
void gotFromDBInternal(Integer holderId, Collection<LeasePath> entityList) {
gotFromDBInternal(entityList);
if (entityList == null || entityList.isEmpty()) {
getPathList(holderId).clear();
}
}
private Set<LeasePath> getPathList(int holderId) {
Set<LeasePath> hopLeasePaths = hIdToLPsMap.get(holderId);
if (hopLeasePaths == null) {
hopLeasePaths = new HashSet<LeasePath>();
hIdToLPsMap.put(holderId, hopLeasePaths);
}
return hopLeasePaths;
}
private void removeFromHIdToLPsMap(LeasePath hopLeasePath) {
Set<LeasePath> hopLeasePaths = getPathList(hopLeasePath.getHolderId());
hopLeasePaths.remove(hopLeasePath);
}
private void addToHIdToLPsMap(LeasePath leasePath) {
Set<LeasePath> hopLeasePaths = getPathList(leasePath.getHolderId());
hopLeasePaths.add(leasePath);
}
private boolean containsInHIdToLPsMap(String path){
for(int hid : hIdToLPsMap.keySet()){
for(LeasePath lp : hIdToLPsMap.get(hid)){
if(lp.getPath().equals(path)){
return true;
}
}
}
return false;
}
private LeasePath getLPFromHIdToLPsMap(String path) throws StorageException{
LeasePath leasePath = null;
int lpCount = 0;
for(int hid : hIdToLPsMap.keySet()){
for(LeasePath lp : hIdToLPsMap.get(hid)){
if(lp.getPath().equals(path)){
lpCount ++;
leasePath = lp;
}
}
}
if(lpCount > 1){
throw new StorageException("A path can be lease only once at a time");
}
return leasePath;
}
class LeasePathPK {
private final String path;
private final int holderId;
public LeasePathPK(String path, int holderId) {
this.path = path;
this.holderId = holderId;
}
public String getPath() {
return path;
}
public int getHolderId() {
return holderId;
}
@Override
public int hashCode() {
int hash = 7;
hash = 97 * hash + (this.path != null ? this.path.hashCode() : 0);
hash = 97 * hash + this.holderId;
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final io.hops.transaction.context.LeasePathContext.LeasePathPK other
= (io.hops.transaction.context.LeasePathContext.LeasePathPK) obj;
if ((this.path == null) ? (other.path != null) : !this.path.equals(other.path)) {
return false;
}
if (this.holderId != other.holderId) {
return false;
}
return true;
}
}
}